├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── README.md ├── V2.0JS.png ├── app.mjs ├── basics ├── lenses-compose │ └── demo.js ├── lenses-intro │ └── demo.js ├── lenses-over copy │ └── demo.js ├── lenses-over │ └── demo.js ├── lenses-ramda │ └── demo.js └── lenses.js ├── common ├── IOToReader.js ├── IOToState.js ├── eitherToPromise.js ├── firstOrNone.js ├── maybeToEither.js ├── maybeToPromise.js ├── optionalToMaybe.js ├── readerToState.js ├── stateToReader.js ├── toEither.js ├── toIO.js ├── toObservable.js └── toPromise.js ├── functors ├── Either.js ├── IO.js ├── Id.js ├── Maybe.js ├── README.md ├── Reader.js ├── State.js ├── algebraic-tree-functor │ ├── Tree.js │ └── demo.js ├── applicative-reader-functor │ └── demo.js ├── composing-functors-adapters │ ├── ComposeT.js │ └── demo.js ├── composing-functors-operators │ ├── demo.js │ └── mapT.js ├── either-functor-example-sanctuary │ ├── data │ │ ├── Db.js │ │ └── Repository.js │ └── demo.js ├── either-functor-example │ ├── data │ │ ├── Db.js │ │ └── Repository.js │ └── demo.js ├── either-functor-state │ ├── data │ │ ├── Db.js │ │ └── Repository.js │ └── demo.js ├── either │ └── demo.js ├── functor-laws-example │ └── demo.js ├── functor-laws │ └── demo.js ├── io │ └── demo.js ├── maybe-functor-example │ ├── data │ │ ├── Db.js │ │ └── Repository.js │ └── demo.js ├── maybe │ └── demo.js ├── model │ └── Client.js ├── observable-functor │ ├── data │ │ ├── Db.js │ │ └── Repository.js │ └── demo.js ├── optional-example │ ├── data │ │ ├── Db.js │ │ └── Repository.js │ └── demo.js ├── promise-as-functor │ └── demo.js ├── promise-functor-example │ ├── data │ │ ├── Db.js │ │ └── Repository.js │ └── demo.js ├── promise-maybe-composition-adapters │ ├── data │ │ └── Repository.js │ ├── demo.js │ └── operators.js ├── promise-maybe-composition │ ├── data │ │ └── Repository.js │ ├── demo.js │ └── operators.js ├── reader-functor │ └── demo.js └── try-functor-sanctuary │ ├── data │ ├── Db.js │ └── Repository.js │ └── demo.js ├── monads ├── Either.js ├── IO.js ├── Id.js ├── Maybe.js ├── Reader.js ├── State.js ├── array-monad │ └── demo.js ├── composeK.js ├── either-monad-example │ ├── data │ │ └── Repositories.js │ └── demo.js ├── either-monad │ └── demo.js ├── fluture-monad-example │ ├── data │ │ └── Repositories.js │ └── demo.js ├── foldM-example │ ├── data │ │ └── Repositories.js │ └── demo.js ├── foldM │ └── demo.js ├── identity-example │ └── demo.js ├── kleisli-composition │ ├── composeK.js │ └── demo.js ├── kleisli-fold │ └── demo.js ├── maybe-kleisli-composition-ramda │ ├── data │ │ └── Repositories.js │ └── demo.js ├── maybe-kleisli-composition │ ├── data │ │ └── Repositories.js │ └── demo.js ├── maybe-monad-example │ ├── data │ │ └── Repositories.js │ └── demo.js ├── maybe-monad │ └── demo.js ├── model │ ├── Client.js │ ├── Db.js │ └── Employee.js ├── promise-maybe-monad-combination-example │ ├── PromiseMaybeT.js │ ├── data │ │ └── Repositories.js │ └── demo.js ├── promise-monad-example │ ├── data │ │ └── Repositories.js │ └── demo.js ├── promise-monad │ └── demo.js ├── state-example-1 │ └── demo.js ├── state-monad │ └── demo.js ├── state-transaction-portofolio-example-crocks │ ├── demo.js │ └── transactions.js └── state-transaction-portofolio-example │ ├── demo.js │ └── transactions.js ├── monoids ├── 1.monoid.demo.js ├── 2.monoid.fluid.demo.js ├── decorator.pattern │ └── demo.js ├── hashmap-monoid │ ├── HashMapMonoid.js │ └── demo.js ├── monoid-homomorphisms │ └── demo.js ├── parenthesis.balancing │ ├── balance.js │ ├── demo.js │ └── parser.js ├── predicate-monoidal-composition │ ├── Product.js │ └── demo.js ├── products-of-monoids │ └── demo.js └── specification-pattern │ ├── Product.js │ ├── Specification.js │ └── demo.js ├── package-lock.json ├── package.json ├── test ├── functors │ └── maybe.js └── transformations │ ├── IOToReader.js │ ├── IOToState.js │ ├── eitherToPromise.js │ ├── firstOrNone.js │ ├── maybeToEither.js │ ├── maybeToPromise.js │ ├── optionalToMaybe.js │ └── stateToReader.js └── transformations └── maybeToPromise └── demo.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "program": "${workspaceFolder}\\app.mjs", 12 | 13 | "runtimeArgs": [ 14 | "--experimental-modules" 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/**/*.js" 18 | ] 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "workbench.colorCustomizations": { 3 | "editor.background": "#f8f8f8" 4 | } 5 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## **Functional Programming in Javascript with Categories** 3 | 4 | Still Adding things at the final installment all of 11 chapters should be visible here 5 | 6 | This is the public repository for the Book : [Functional Programming in Javascript with Categories](https://leanpub.com/functional-programming-in-js-with-categories) 7 | 8 | this is the initial commit for the version 2.0.0 book update. It uses the import syntax by utilizing of the "--experimental-modules". Make sure if you use VS code there is the .vscode folder with the launch.json that contains the settings for the "--experimental-modules" 9 | 10 | ![enter image description here](https://github.com/dimitris-papadimitriou-chr/FunctionalJsWithCategories/blob/master/V2.0JS.png ) 11 | -------------------------------------------------------------------------------- /V2.0JS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimitris-papadimitriou-chr/FunctionalJsWithCategories/627401e693e9e94bc93720f4f539407bdad5843f/V2.0JS.png -------------------------------------------------------------------------------- /app.mjs: -------------------------------------------------------------------------------- 1 | 2 | import { demo } from './monads/promise-maybe-monad-combination-example/demo.js' 3 | 4 | demo(); 5 | -------------------------------------------------------------------------------- /basics/lenses-compose/demo.js: -------------------------------------------------------------------------------- 1 | import { lens, view, set, composeLenses } from "../lenses.js"; 2 | 3 | export function demo() { 4 | 5 | var clientEmployeeLens = lens(`employee`); 6 | var employeeAgeLens = lens(`age`); 7 | 8 | let client = { 9 | name: "Jim", 10 | age: 25, 11 | employee: { 12 | name: `john`, 13 | age: 25 14 | } 15 | }; 16 | 17 | var clientEmployeeAgeLens = composeLenses(employeeAgeLens, clientEmployeeLens); 18 | 19 | { 20 | console.log(view(clientEmployeeAgeLens, client)); //view 21 | 22 | var updatedClient = set(clientEmployeeAgeLens, client, 30); //update 23 | console.log(view(clientEmployeeAgeLens, updatedClient)); //view 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /basics/lenses-intro/demo.js: -------------------------------------------------------------------------------- 1 | 2 | export function demo() { 3 | { 4 | //Mutable 5 | //ad-hoc lens - specificly creafted for the name property 6 | const nameLens = { 7 | get: client => client.name, 8 | set: (client, newName) => { 9 | client.name = newName; //we mutate the object and return void 10 | } //copy object with updated name value 11 | }; 12 | 13 | let client = { name: "Jim", age: 25 }; 14 | 15 | console.log(nameLens.get(client)); //getter 16 | 17 | nameLens.set(client, `Jim doe`); //setter - update object with value 18 | console.log(nameLens.get(client)); //getter on the updatedClient 19 | } 20 | 21 | { 22 | //Immutable 23 | //ad-hoc lens - specificly creafted for the name property 24 | const nameLens = { 25 | get: client => client.name, 26 | set: (client, newName) => ({ ...client, [`name`]: newName }) //copy object with updated name value 27 | }; 28 | 29 | let client = { name: "Jim", age: 25 }; 30 | 31 | console.log(nameLens.get(client)); //getter 32 | 33 | let updatedClient = nameLens.set(client, `Jim doe`); //setter - update object with value 34 | console.log(nameLens.get(updatedClient)); //getter on the updatedClient 35 | } 36 | 37 | { 38 | //generic lens 39 | const lens = key => ({ 40 | get: obj => obj[key], 41 | set: (obj, value) => ({ ...obj, [key]: value }) 42 | }); 43 | 44 | var nameLens = lens(`name`); 45 | 46 | let client = { name: "Jim", age: 25 }; 47 | 48 | console.log(nameLens.get(client)); //getter 49 | 50 | let updatedClient = nameLens.set(client, `Jim doe`); //setter - update object with value 51 | console.log(nameLens.get(updatedClient)); //getter on the updatedClient 52 | } 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /basics/lenses-over copy/demo.js: -------------------------------------------------------------------------------- 1 | import { lens, view, over, } from "../lenses.js"; 2 | 3 | export function demo() { 4 | 5 | var nameLens = lens(`name`); 6 | let client = { name: "Jim", age: 25 }; 7 | 8 | var updatedClient = over(nameLens, x => x.toUpperCase(), client); 9 | 10 | console.log(view(nameLens, updatedClient)); 11 | } 12 | -------------------------------------------------------------------------------- /basics/lenses-over/demo.js: -------------------------------------------------------------------------------- 1 | import { lens, view, over, } from "./../lenses.js"; 2 | 3 | export function demo() { 4 | 5 | var nameLens = lens(`name`); 6 | let client = { name: "Jim", age: 25 }; 7 | 8 | var updatedClient = over(nameLens, x => x.toUpperCase(), client); 9 | 10 | console.log(view(nameLens, updatedClient)); 11 | } 12 | -------------------------------------------------------------------------------- /basics/lenses-ramda/demo.js: -------------------------------------------------------------------------------- 1 | import R from "ramda"; 2 | 3 | export function demo() { 4 | 5 | let client = { name: "Jim", age: 25 }; 6 | 7 | { 8 | //using R.lensProp 9 | var nameLens = R.lensProp(`name`); 10 | 11 | console.log(R.view(nameLens, client)); //getter 12 | 13 | let updatedClient = R.set(nameLens, `Jim doe`, client); //setter 14 | console.log(R.view(nameLens, updatedClient)); //getter 15 | } 16 | 17 | { 18 | //constructing explicitly the equivalent of R.lensProp 19 | var nameLens = R.lens(R.prop("name"), R.assoc("name")); //same as R.lensProp(`name`) 20 | 21 | console.log(R.view(nameLens, client)); //getter 22 | 23 | let updatedClient = R.set(nameLens, `Jim doe`, client); //setter 24 | console.log(R.view(nameLens, updatedClient)); //getter 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /basics/lenses.js: -------------------------------------------------------------------------------- 1 | //generic lens 2 | export const lens = key => ({ 3 | get: obj => obj[key], 4 | set: (obj, value) => ({ ...obj, [key]: value }) 5 | }); 6 | 7 | export let view = (lens, obj) => lens.get(obj); 8 | export let set = (lens, obj, value) => lens.set(obj, value); 9 | export let over = (lens, map, obj) => lens.set(obj, map(lens.get(obj))); 10 | 11 | export const composeLenses = (lens1, lens2) => ({ 12 | get: obj => lens1.get(lens2.get(obj)), 13 | set: (obj, value) => lens2.set(obj, lens1.set(lens2.get(obj), value)) 14 | }); 15 | -------------------------------------------------------------------------------- /common/IOToReader.js: -------------------------------------------------------------------------------- 1 | import { Reader } from "../monads/Reader.js" 2 | 3 | export let IOToReader = io => Reader(env => io.run()) //discard environment env 4 | 5 | 6 | -------------------------------------------------------------------------------- /common/IOToState.js: -------------------------------------------------------------------------------- 1 | import { State } from "../monads/State.js" 2 | 3 | export let IOToState = io => State(s => ({ value: io.run(), state: s })) -------------------------------------------------------------------------------- /common/eitherToPromise.js: -------------------------------------------------------------------------------- 1 | export let eitherToPromise = either => new Promise((resolve, reject) => { 2 | either.match({ 3 | right: value => resolve(value), 4 | left: error => reject(error) 5 | }) 6 | }) 7 | 8 | -------------------------------------------------------------------------------- /common/firstOrNone.js: -------------------------------------------------------------------------------- 1 | import { Some, None } from "./../monads/Maybe.js" 2 | 3 | export let firstOrNone = array => array.length > 0 ? Some(array[0]) : None(); 4 | 5 | -------------------------------------------------------------------------------- /common/maybeToEither.js: -------------------------------------------------------------------------------- 1 | import { Left, Right } from "../monads/Either.js" 2 | 3 | //here we placed the left reason as first curried parameter. 4 | export let maybeToEither = defaultLeft => 5 | maybe => maybe.match({ 6 | some: v => Right(v), 7 | none: () => Left(defaultLeft) 8 | }) 9 | 10 | -------------------------------------------------------------------------------- /common/maybeToPromise.js: -------------------------------------------------------------------------------- 1 | export let maybeToPromise = defaultReject => 2 | maybe => new Promise((resolve, reject) => { 3 | maybe.match({ 4 | some: v => resolve(v), 5 | none: () => reject(defaultReject) 6 | }) 7 | }) 8 | 9 | -------------------------------------------------------------------------------- /common/optionalToMaybe.js: -------------------------------------------------------------------------------- 1 | import { Some, None } from "../monads/Maybe.js" 2 | 3 | export let optionalToMaybe = value => value ? Some(value) : None(); 4 | 5 | -------------------------------------------------------------------------------- /common/readerToState.js: -------------------------------------------------------------------------------- 1 | import { Reader } from "../monads/Reader.js" 2 | import { State } from "../monads/State.js" 3 | 4 | /** run reader with the state as environment and then use the same state as state */ 5 | 6 | export let readerToState = reader => State(state => ({ value: reader.run(state).value, state: state })) 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /common/stateToReader.js: -------------------------------------------------------------------------------- 1 | import { Reader } from "../monads/Reader.js" 2 | 3 | /** run state with the enviroment as state and then treun the value */ 4 | 5 | export let stateToReader = state => Reader(env => state.run(env).value) 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /common/toEither.js: -------------------------------------------------------------------------------- 1 | import { Left, Right } from "./../monads/Either.js" 2 | 3 | //here we placed the left reason as first curried parameter. 4 | export let toEither = defaultLeft => 5 | maybe => maybe.match({ 6 | some: v => Right(v), 7 | none: () => Left(defaultLeft) 8 | }) 9 | 10 | -------------------------------------------------------------------------------- /common/toIO.js: -------------------------------------------------------------------------------- 1 | import { IO } from "./../monads/IO.js" 2 | 3 | export let toIO = value => new IO(() => value) 4 | 5 | 6 | -------------------------------------------------------------------------------- /common/toObservable.js: -------------------------------------------------------------------------------- 1 | 2 | import Rx from "rxjs"; 3 | 4 | export let toObservable = defaultError => 5 | maybe => new Rx.Observable(subscriber => { 6 | maybe.match({ 7 | some: v => subscriber.next(v), 8 | none: () => subscriber.error(defaultError) 9 | }); 10 | }); 11 | 12 | -------------------------------------------------------------------------------- /common/toPromise.js: -------------------------------------------------------------------------------- 1 | export let toPromise = defaultReject => 2 | maybe => new Promise((resolve, reject) => { 3 | maybe.match({ 4 | some: v => resolve(v), 5 | none: () => reject(defaultReject) 6 | }) 7 | }) 8 | 9 | -------------------------------------------------------------------------------- /functors/Either.js: -------------------------------------------------------------------------------- 1 | export let Right = (v) => ({ 2 | map: (f) => Right(f(v)), 3 | match: (pattern) => pattern.right(v) 4 | }); 5 | 6 | export let Left = (v) => ({ 7 | map: (_) => Left(v), 8 | match: (pattern) => pattern.left(v) 9 | }); 10 | -------------------------------------------------------------------------------- /functors/IO.js: -------------------------------------------------------------------------------- 1 | export let IO = fn => ({ 2 | map: f => IO(() => f(fn())), 3 | run: () => fn(), //aka getValue() 4 | match: pattern => pattern(fn()) //we could define a match 5 | }) -------------------------------------------------------------------------------- /functors/Id.js: -------------------------------------------------------------------------------- 1 | export let Id = v => ({ 2 | getValue: () => v, 3 | map: f => Id(f(v)) 4 | }); 5 | -------------------------------------------------------------------------------- /functors/Maybe.js: -------------------------------------------------------------------------------- 1 | export let Some = (v) => ({ 2 | map: (f) => Some(f(v)), 3 | match: pattern => pattern.some(v), 4 | }); 5 | 6 | export let None = () => ({ 7 | map: (_) => None(), 8 | match: pattern => pattern.none(), 9 | }); 10 | -------------------------------------------------------------------------------- /functors/README.md: -------------------------------------------------------------------------------- 1 | ## What is a Functor 2 | 3 | In JavaScript one the most famous functional programming idea is to use `Array.map` to replace `for` loops iterations. That is because an array is a Functor, which is a more abstract idea that we will explore in this section. 4 | 5 | > **Practically a Functor is a container that has a valid `.map()` method** 6 | 7 | We will start by looking at the minimum structure that qualifies as a functor in javascript: 8 | 9 | ```javascript 10 | const Id = (v) => ({ 11 | value: v, 12 | map: (f) => Id(f(v)) 13 | }); 14 | 15 | ``` 16 | 17 | 18 | 19 | This is the minimum construction that we could call a functor because it has exactly two properties: 20 | 21 | 1. A “constructor” that maps a value v to an object Id(v). Places the value in the container. 22 | 2. And it has a mapping method map(f) that takes a function f:a→b and transforms an Id(a) to an Id(b) 23 | 24 | 25 | 26 | - In our case the container is just the object literal `({ _:_ })`, and we can place something inside. The first arrow that places items inside the container is the constructor of the Object. 27 | 28 | - In our notation we don’t have a constructor but the lambda arrow is the equivalent of a constructor `v=>({ _:v })`. If you think about it, a constructor is just a function that takes some parameters as inputs and gives you an object. 29 | 30 | ## Chain Computations with the Id Functor 31 | 32 | The Id Functor is very simple and doesn’t do that much. *Nonetheless it allows us to **chain computations** using sequential `.map()` computations*: 33 | 34 | ```javascript 35 | 36 | const Id = (v) => ({ 37 | getValue: () => v, 38 | map: (f) => Id(f(v)) 39 | }); 40 | 41 | var result = Id(2) 42 | .map(x => x * x) 43 | .map(x => x.toString()); 44 | 45 | console.log(result.getValue()); 46 | ``` 47 | 48 | [Run this](https://stackblitz.com/edit/the-identity-functor-1?file=index.js) 49 | 50 | 51 | For a more realistic example usage of the chaining property of the map you can see the following snippet : 52 | 53 | ```javascript 54 | import { Id } from "./Id.js" 55 | import { Client } from "./Client.js" 56 | 57 | var upperCaseName = Id(new Client(1,"jake")) 58 | .map(client=>client.name) 59 | .map(name=>name.toUpperCase()) 60 | .getValue(); 61 | 62 | console.log(upperCaseName); 63 | 64 | ``` 65 | 66 | [Run this](https://stackblitz.com/edit/the-identity-functor-2?file=index.js) -------------------------------------------------------------------------------- /functors/Reader.js: -------------------------------------------------------------------------------- 1 | export let Reader = (expr) => ({ 2 | expr: expr, 3 | map: f => Reader(env => f(Reader(expr).run(env))), 4 | run: env => expr(env), 5 | ap: reader => Reader(env => Reader(expr).run(env)(reader.run(env))) 6 | }); -------------------------------------------------------------------------------- /functors/State.js: -------------------------------------------------------------------------------- 1 | export var State = expression => ({ 2 | map: f => 3 | State(environment => { 4 | var { value: value, state: state } = expression(environment); 5 | return { value: f(value), state: state }; 6 | }), 7 | run: environment => expression(environment) 8 | }); 9 | -------------------------------------------------------------------------------- /functors/algebraic-tree-functor/Tree.js: -------------------------------------------------------------------------------- 1 | export class Tree { } 2 | 3 | export class Leaf extends Tree { 4 | constructor(value) { 5 | super() 6 | this.value = value; 7 | } 8 | map(f) { 9 | return new Leaf(f(this.value)); 10 | } 11 | show() { 12 | return `Leaf(${this.value})` //for display purposes 13 | } 14 | } 15 | 16 | export class Node extends Tree { 17 | constructor(left, right) { 18 | super() 19 | this.left = left; 20 | this.right = right; 21 | } 22 | map(f) { 23 | return new Node(this.left.map(f), this.right.map(f)); 24 | } 25 | 26 | show() { 27 | return `Node(${this.left.show()},${this.right.show()})`; //for display purposes 28 | } 29 | } -------------------------------------------------------------------------------- /functors/algebraic-tree-functor/demo.js: -------------------------------------------------------------------------------- 1 | import { Node, Leaf } from "./Tree.js" 2 | 3 | export function demo() { 4 | 5 | var instance = new Node(new Node(new Leaf(4), new Leaf(5)), new Leaf(7)); 6 | 7 | console.log(instance.show()); 8 | 9 | console.log(`after using map`); 10 | 11 | console.log(instance.map(x => x + 1).show()); 12 | } 13 | -------------------------------------------------------------------------------- /functors/applicative-reader-functor/demo.js: -------------------------------------------------------------------------------- 1 | import { Reader } from "../Reader.js" 2 | 3 | export function demo() { 4 | 5 | { 6 | var ENV = {}; 7 | var res = Reader(env => x => y => y + x).ap(Reader(env => 1)).ap(Reader(env => 2)).run(ENV); 8 | console.log(res); 9 | 10 | var ENV = { x: 1, y: 2, z: 3 }; 11 | var res = Reader(env => x => y => y + x + env.z).ap(Reader(env => env.x)).ap(Reader(env => env.y)).run(ENV); 12 | console.log(res); 13 | } 14 | 15 | { 16 | var Product = (price) => ({ 17 | getFinalPrice(pricingStrategy) { 18 | return pricingStrategy(price) 19 | } 20 | }) 21 | 22 | var CONFIG = { discount: 0.1, productPrice: 100 }; 23 | 24 | var discountedPrice = 25 | Reader(config => product => product.getFinalPrice(price => price * (1 - config.discount))) 26 | .ap(Reader(config => Product(config.productPrice))) 27 | .run(CONFIG); //90 28 | 29 | console.log(discountedPrice) 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /functors/composing-functors-adapters/ComposeT.js: -------------------------------------------------------------------------------- 1 | export const ComposeT = composition => ({ 2 | map: f => ComposeT(composition.map(inner => inner.map(f))), 3 | unwrap: () => composition 4 | }); 5 | -------------------------------------------------------------------------------- /functors/composing-functors-adapters/demo.js: -------------------------------------------------------------------------------- 1 | import { Id } from "../Id.js" 2 | import { Client } from "../model/Client.js" 3 | import { ComposeT } from "./ComposeT.js" 4 | 5 | 6 | export function demo() { 7 | 8 | let mapT = f => composite => composite.map(inner => inner.map(f)); 9 | 10 | // an Array in an Id composition 11 | { 12 | let idClients = Id([new Client(1, "jim", 2), new Client(2, "jane", 3)]); 13 | 14 | let idNames = ComposeT(idClients) 15 | .map(client => client.name) 16 | .map(name => name.toUpperCase()) 17 | .unwrap(); //Id> 18 | 19 | console.log(JSON.stringify(idNames.getValue())); //Id (['JIM', 'JANE' ] ) 20 | //the result is an Id 21 | // -An Id of an Array of Strings 22 | } 23 | 24 | 25 | // an Array in an Array composition 26 | { 27 | let groupsOfClientsByDepartment = [ 28 | [new Client(1, "jim", 2), new Client(2, "jane", 3)], 29 | [new Client(3, "kate", 2), new Client(4, "John", 3)] 30 | ]; 31 | 32 | let groupsOfClientNamesByDepartment = ComposeT(groupsOfClientsByDepartment) 33 | .map(client => client.name) 34 | .map(name => name.toUpperCase()) 35 | .unwrap(); //Id> 36 | 37 | console.log(JSON.stringify(groupsOfClientNamesByDepartment)); //[["JIM","JANE"],["KATE","JOHN"]] 38 | 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /functors/composing-functors-operators/demo.js: -------------------------------------------------------------------------------- 1 | import { Id } from "../Id.js" 2 | import { Client } from "../model/Client.js" 3 | import { mapT } from "./mapT.js" 4 | 5 | export function demo() { 6 | 7 | // an Array in an Id composition 8 | { 9 | let idClients = Id([new Client(1, "jim", 2), new Client(2, "jane", 3)]); 10 | 11 | let idNames = mapT(client => client.name)(idClients); //Id> 12 | 13 | console.log(JSON.stringify(idNames.getValue())); //Id (['jim', 'jane' ] ) 14 | //the result is an Id -An Id of an Array of Strings 15 | } 16 | 17 | 18 | // an Array in an Array composition 19 | { 20 | let groupsOfClientsByDepartment = [ 21 | [new Client(1, "jim", 2), new Client(2, "jane", 3)], 22 | [new Client(3, "kate", 2), new Client(4, "John", 3)] 23 | ]; 24 | 25 | var groupsOfClientNamesByDepartment = mapT(client => client.name)(groupsOfClientsByDepartment); 26 | 27 | console.log(groupsOfClientNamesByDepartment); //[ [ 'jim', 'jane' ], [ 'kate', 'John' ] ] 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /functors/composing-functors-operators/mapT.js: -------------------------------------------------------------------------------- 1 | export let mapT = f => composite => composite.map(inner => inner.map(f)); -------------------------------------------------------------------------------- /functors/either-functor-example-sanctuary/data/Db.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../../model/Client.js" 2 | 3 | export let Db = { //mock client Db as Singleton 4 | Clients: [ 5 | new Client(1, "jim"), 6 | new Client(2, "jane") 7 | ] 8 | }; -------------------------------------------------------------------------------- /functors/either-functor-example-sanctuary/data/Repository.js: -------------------------------------------------------------------------------- 1 | 2 | import { firstOrNone } from "../../../common/firstOrNone.js" 3 | import { maybeToEither } from "../../../common/maybeToEither.js" 4 | import { Db } from "./Db.js" 5 | import S from "sanctuary"; 6 | 7 | // export let ClientRepository = { 8 | // getById: id => //returns an Either 9 | // maybeToEither(`no client found`) 10 | // (firstOrNone(Db.Clients.filter(client => client.id == id))) 11 | 12 | // }; 13 | 14 | // A ramda repository that everything is composed using R.comspose. Is equivalent with the one above 15 | // export let ClientRepository = { 16 | // getById: id => S.compose( //returns an Either 17 | // maybeToEither(`no client found`), 18 | // firstOrNone, 19 | // S.filter(client => client.id == id) //this is equivalent to -> R.filter(client => client.id == id) 20 | // )(Db.Clients) 21 | // }; 22 | 23 | export let ClientRepository = { 24 | getById: id => S.maybeToEither(`no client found`) 25 | ( 26 | S.head( 27 | S.filter(client => client.id == id)(Db.Clients) 28 | ) 29 | ) 30 | }; 31 | 32 | -------------------------------------------------------------------------------- /functors/either-functor-example-sanctuary/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | import { ClientRepository } from "./data/Repository.js" 3 | import S from "sanctuary"; 4 | 5 | export function demo() { 6 | 7 | var getClientNameById = id => 8 | S.either(e => `error : ${e}`) 9 | (result => `client name:${result}`) 10 | (S.map(Client.name)(ClientRepository.getById(id))); 11 | 12 | 13 | console.log(`results`) 14 | console.log(getClientNameById(1)); 15 | console.log(getClientNameById(2)); 16 | console.log(getClientNameById(3)); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /functors/either-functor-example/data/Db.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../../model/Client.js" 2 | 3 | export let Db = { //mock client Db as Singleton 4 | Clients: [ 5 | new Client(1, "jim"), 6 | new Client(2, "jane") 7 | ] 8 | }; -------------------------------------------------------------------------------- /functors/either-functor-example/data/Repository.js: -------------------------------------------------------------------------------- 1 | 2 | import { firstOrNone } from "../../../common/firstOrNone.js" 3 | import { maybeToEither } from "../../../common/maybeToEither.js" 4 | import { Db } from "./Db.js" 5 | import R from 'ramda' 6 | 7 | 8 | // export let ClientRepository = { 9 | // getById: id => //returns an Either 10 | // toEither(`no client found`) 11 | // (firstOrNone(Db.Clients.filter(client => client.id == id))) 12 | 13 | // }; 14 | 15 | // A ramda repository that everything is composed using R.comspose. Is equivalent with the one above 16 | export let ClientRepository = { 17 | getById: id => R.compose( //returns an Either 18 | maybeToEither(`no client found`), 19 | firstOrNone, 20 | R.filter(R.propEq('id', id)) //this is equivalent to -> R.filter(client => client.id == id) 21 | )(Db.Clients) 22 | }; 23 | -------------------------------------------------------------------------------- /functors/either-functor-example/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | import { ClientRepository } from "./data/Repository.js" 3 | 4 | export function demo() { 5 | 6 | let getClientNameById = id => 7 | ClientRepository.getById(id) 8 | .map(Client.name) 9 | .match({ 10 | right: value => `client name: ${value}`, 11 | left: error => `error: ${error}` 12 | }); 13 | 14 | console.log(`results`) 15 | console.log(getClientNameById(1)); 16 | console.log(getClientNameById(2)); 17 | console.log(getClientNameById(3)); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /functors/either-functor-state/data/Db.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../../model/Client.js" 2 | 3 | export let Db = { //mock client Db as Singleton 4 | Clients: [ 5 | new Client(1, "jim"), 6 | new Client(2, "jane") 7 | ] 8 | }; -------------------------------------------------------------------------------- /functors/either-functor-state/data/Repository.js: -------------------------------------------------------------------------------- 1 | 2 | import { firstOrNone } from "../../../common/firstOrNone.js" 3 | import { maybeToEither } from "../../../common/maybeToEither.js" 4 | 5 | import { State } from "./../../state.js"; 6 | 7 | 8 | 9 | export let ClientRepository = { 10 | getById: id => State(s => { 11 | return ({ 12 | value: s.state.Clients.filter(client => client.id == id), 13 | state: s 14 | }) 15 | }) 16 | .map(firstOrNone) 17 | .map(maybeToEither(`no client found`)) 18 | //.run({ state: Db }).value 19 | //returns an Either 20 | 21 | }; 22 | 23 | 24 | -------------------------------------------------------------------------------- /functors/either-functor-state/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | import { ClientRepository } from "./data/Repository.js" 3 | import { State } from "./../state.js"; 4 | 5 | 6 | export function demo() { 7 | 8 | var maybeClient = ClientRepository.getById(1); 9 | 10 | var fold = maybeClient 11 | .map(m => m.map(c => c.name)) 12 | .run({ state: Db }) 13 | .value 14 | .match({ 15 | right: value => `employee name: ${value}`, 16 | left: error => `error: ${error}` 17 | }); 18 | 19 | console.log(fold); 20 | 21 | { 22 | // var stateMaybeClient = State(env => { 23 | // return { 24 | // value: maybeClient 25 | // // .match({ 26 | // // right: value => env.state.titleTemplate(value), 27 | // // left: error => `error: ${error}` 28 | // // }) 29 | // , 30 | // state: env.state 31 | // }; 32 | // }); 33 | 34 | // var fold = stateMaybeClient 35 | // .map(m => m.map(c => c.name)) 36 | // .run({ state: { titleTemplate: s => `the employee name is ${s}` } }) 37 | // .value 38 | // .match({ 39 | // right: value => `employee name: ${value}`, 40 | // left: error => `error: ${error}` 41 | // }); 42 | 43 | // console.log(fold); 44 | } 45 | 46 | 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /functors/either/demo.js: -------------------------------------------------------------------------------- 1 | import { Left, Right } from "./../Either.js" 2 | import { Client } from "../model/Client.js" 3 | 4 | export function demo() { 5 | 6 | { 7 | //1. the right case 8 | 9 | let result = Right(new Client(1, "jake")) 10 | .map(Client.name) 11 | .match({ 12 | right: value => `right: ${value}`, //we reach this case 13 | left: error => `left: ${error}` 14 | }) 15 | console.log(result); 16 | } 17 | 18 | 19 | { 20 | //2. the left case 21 | 22 | let result = Left('no client found') 23 | .map(Client.name) //the left map ignores the transformations 24 | .match({ 25 | right: value => `right: ${value}`, 26 | left: error => `left: ${error}` //we reach this case 27 | }) 28 | console.log(result); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /functors/functor-laws-example/demo.js: -------------------------------------------------------------------------------- 1 | import { Id } from "../Id.js"; 2 | import { Client } from "../model/Client.js" 3 | 4 | export function demo() { 5 | 6 | var getName = client => client.name; 7 | var toUpperCase = name => name.toUpperCase(); 8 | var composition = client => toUpperCase(getName(client)); 9 | 10 | var client = new Client(1, "jake"); 11 | 12 | //all the following are equivalent : 13 | console.log(composition(client)) 14 | 15 | console.log(Id(composition(client)).getValue()); 16 | console.log(Id(composition(client)).map(x => x).getValue()); //F(f○g) ○ 1 17 | console.log(Id(client).map(composition).getValue()); //F(f○g) 18 | console.log(Id(client).map(getName).map(toUpperCase).getValue()); //F(f) ○ F(g) 19 | console.log(Id(client).map(composition).map(x => x).getValue()); //F(f) ○ F(g) ○ 1 20 | 21 | } 22 | -------------------------------------------------------------------------------- /functors/functor-laws/demo.js: -------------------------------------------------------------------------------- 1 | import { Id } from "../Id.js"; 2 | 3 | 4 | export function demo() { 5 | 6 | var id = x => x; 7 | var value = 5 8 | 9 | // Law 1 - identity preserving 10 | console.log(Id(value).map(id).getValue() === id(value)) 11 | 12 | // Law 2 composition of functions is preserved 13 | 14 | // for any f and g 15 | var f = x => 2 * x; 16 | var g = x => 3 * x; 17 | 18 | console.log(Id(value).map(x => f(g(x))).getValue() === Id(value).map(f).map(g).getValue()); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /functors/io/demo.js: -------------------------------------------------------------------------------- 1 | import { IO } from "../IO.js"; 2 | import { Client } from "./Client.js" 3 | 4 | export function demo() { 5 | 6 | 7 | 8 | } 9 | -------------------------------------------------------------------------------- /functors/maybe-functor-example/data/Db.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../../model/Client.js" 2 | 3 | export let Db = { //mock client Db as Singleton 4 | Clients: [ 5 | new Client(1, "jim"), 6 | new Client(2, "jane") 7 | ] 8 | }; -------------------------------------------------------------------------------- /functors/maybe-functor-example/data/Repository.js: -------------------------------------------------------------------------------- 1 | 2 | import { firstOrNone } from "../../../common/firstOrNone.js" 3 | import { Db } from "./Db.js" 4 | 5 | export let ClientRepository = { 6 | getById: id => firstOrNone(Db.Clients.filter(client => client.id == id)) 7 | //returns a Maybe 8 | }; -------------------------------------------------------------------------------- /functors/maybe-functor-example/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | import { ClientRepository } from "./data/Repository.js" 3 | 4 | export function demo() { 5 | 6 | ClientRepository.getById(2) //returns a Maybe 7 | .map(Client.name) //Maybe 8 | .match({ //String 9 | some: name => console.log(`client name:${name}`), 10 | none: () => console.log(`no client found`) 11 | }); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /functors/maybe/demo.js: -------------------------------------------------------------------------------- 1 | import { Some, None } from "./../Maybe.js" 2 | import { Client } from "../model/Client.js" 3 | 4 | export function demo() { 5 | 6 | { 7 | //non-null case 8 | 9 | let result = Some(new Client(1, "jake")) 10 | .map(client => client.name) 11 | .match({ 12 | some: v => `client name:${v}`, 13 | none: () => `no client found` 14 | }); 15 | 16 | console.log(result); 17 | } 18 | 19 | 20 | { 21 | //null case 22 | 23 | let result = None() 24 | .map(Client.name) 25 | .match({ 26 | some: v => `client name:${v}`, 27 | none: () => `no client found` 28 | }); 29 | console.log(result); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /functors/model/Client.js: -------------------------------------------------------------------------------- 1 | export class Client { 2 | constructor(id, name) { 3 | this.id = id; 4 | this._name = name; 5 | } 6 | get name() { 7 | //getter and setters : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get 8 | return this._name; 9 | } 10 | set name(x) { 11 | this._name = x; 12 | } 13 | 14 | static get name() { 15 | return c => c._name; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /functors/observable-functor/data/Db.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../../model/Client.js" 2 | 3 | export let Db = { //mock client Db as Singleton 4 | Clients: [ 5 | new Client(1, "jim"), 6 | new Client(2, "jane") 7 | ] 8 | }; -------------------------------------------------------------------------------- /functors/observable-functor/data/Repository.js: -------------------------------------------------------------------------------- 1 | 2 | import { firstOrNone } from "../../../common/firstOrNone.js" 3 | import { Db } from "./Db.js" 4 | import R from 'ramda' 5 | import { toObservable } from "../../../common/toObservable.js"; 6 | 7 | export let ClientRepository = { 8 | getById: id => R.compose( //returns an Either 9 | toObservable(`no client found`), 10 | firstOrNone, 11 | R.filter(R.propEq('id', id)) //this is equivalent to -> R.filter(client => client.id == id) 12 | )(Db.Clients) 13 | }; 14 | -------------------------------------------------------------------------------- /functors/observable-functor/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | import { ClientRepository } from "./data/Repository.js" 3 | 4 | import Operators from "../../node_modules/rxjs/operators/index.js"; //this is a sortcut to import rxjs module using 5 | //experimantal modules in order to use the import {} syntax 6 | const { map, } = Operators; 7 | 8 | export function demo() { 9 | 10 | var getClientNameById = id => 11 | ClientRepository.getById(id) 12 | .pipe( 13 | map(Client.name), 14 | map(name => `client name:${name}`) 15 | ) 16 | .subscribe({ // this is the pattern matching 17 | next: x => console.log(x), 18 | error: err => console.log(err), 19 | complete: () => console.log("done") 20 | }); 21 | 22 | getClientNameById(1); 23 | getClientNameById(2); 24 | getClientNameById(3); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /functors/optional-example/data/Db.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../../model/Client.js" 2 | 3 | export let Db = { //mock client Db as Singleton 4 | Clients: [ 5 | new Client(1, "jim"), 6 | new Client(2, "jane") 7 | ] 8 | }; -------------------------------------------------------------------------------- /functors/optional-example/data/Repository.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { Db } from "./Db.js" 4 | 5 | export let ClientRepository = { 6 | getById: id => Db.Clients.find(client => client.id == id) 7 | //returns a Maybe 8 | }; -------------------------------------------------------------------------------- /functors/optional-example/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | import { ClientRepository } from "./data/Repository.js" 3 | 4 | export function demo() { 5 | 6 | let getClientNameById = id => { 7 | var client = ClientRepository.getById(id); 8 | returnclient ? `client name:${client?.name?.toUpperCase()}` 9 | : `no client found`; 10 | }; 11 | 12 | log(getClientNameById(new Client(1, "jake"))); 13 | log(getClientNameById(null)); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /functors/promise-as-functor/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | 3 | 4 | export function demo() { 5 | 6 | // in order to make obvious the different promise.then() behaviors we can normalize 7 | //the pattern matching operations of the promise by defining: 8 | Promise.prototype.match = function (pattern) { 9 | return this.then(pattern.right).catch(pattern.left); 10 | }; 11 | 12 | 13 | //the successful path 14 | { 15 | Promise.resolve(new Client(1, "jim", 2)) 16 | .then(x => x.name) //this then functions like .map() 17 | .match({ 18 | right: value => console.log(`client name: ${value}`), 19 | left: error => console.log(`error: ${error}`) 20 | }); 21 | } 22 | 23 | {//the failed path 24 | // Promise.reject bypass the first then and goes to the catch 25 | Promise.reject(`nothing found`) 26 | .then(x => x.name) //this then functions like .map() 27 | .match({ 28 | right: value => console.log(`client name: ${value}`), 29 | left: error => console.log(`error: ${error}`) 30 | }); 31 | 32 | } 33 | 34 | 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /functors/promise-functor-example/data/Db.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../../model/Client.js" 2 | 3 | export let Db = { //mock client Db as Singleton 4 | Clients: [ 5 | new Client(1, "jim"), 6 | new Client(2, "jane") 7 | ] 8 | }; -------------------------------------------------------------------------------- /functors/promise-functor-example/data/Repository.js: -------------------------------------------------------------------------------- 1 | 2 | import { firstOrNone } from "../../../common/firstOrNone.js" 3 | import { Db } from "./Db.js" 4 | import R from 'ramda' 5 | import { maybeToPromise } from "../../../common/maybeToPromise.js"; 6 | 7 | 8 | export let ClientRepository = { 9 | getById: id => R.compose( //returns an Either 10 | maybeToPromise(`no client found`), 11 | firstOrNone, 12 | R.filter(R.propEq('id', id)) //this is equivalent to -> R.filter(client => client.id == id) 13 | )(Db.Clients) 14 | }; 15 | -------------------------------------------------------------------------------- /functors/promise-functor-example/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | import { ClientRepository } from "./data/Repository.js" 3 | 4 | export function demo() { 5 | 6 | let getClientNameById = id => //Promise 7 | ClientRepository.getById(id) ///Promise 8 | .then(Client.name) //Promise after the then().catch() patternmatching pair 9 | .then(value => `employee name: ${value}`) 10 | .catch(error => `error: ${error}`); 11 | 12 | console.log(`results`) 13 | 14 | getClientNameById(1).then(result => console.log(`id=1: ${result}`)); 15 | getClientNameById(2).then(result => console.log(`id=2: ${result}`)); 16 | getClientNameById(3).then(result => console.log(`id=3: ${result}`)); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /functors/promise-maybe-composition-adapters/data/Repository.js: -------------------------------------------------------------------------------- 1 | 2 | import { firstOrNone } from "../../../common/firstOrNone.js" 3 | import fetch from 'node-fetch' 4 | 5 | export var ClientRepository = ({ 6 | getById: (id) => // returns a Promise> Type 7 | fetch("https://run.mocky.io/v3/a1678fa6-d99f-4502-939e-44dcd1479f9d") 8 | .then(response => response.json()) 9 | .then(data => data.clients.filter(client => client.id == id)) 10 | .then(firstOrNone) 11 | 12 | }); 13 | 14 | -------------------------------------------------------------------------------- /functors/promise-maybe-composition-adapters/demo.js: -------------------------------------------------------------------------------- 1 | import { ClientRepository } from "./data/Repository.js" 2 | import { PromiseMaybeT } from "./operators.js" 3 | import R from 'Ramda' 4 | 5 | export function demo() { 6 | 7 | { //initial 8 | ClientRepository.getById(1) 9 | .then(maybeClient => maybeClient.map(client => client.name)) 10 | .then( 11 | client => client.match({ 12 | some: name => console.log(`client name:${name}`), 13 | none: () => console.log(`no client found`) 14 | }) 15 | ) 16 | .catch(e => console.log(`fetch operation failed ${e}`)) 17 | } 18 | 19 | {//refactored using PromiseMaybeT adapter - this is not a "monad transformer" in the strict functional term but acts in a similar manner 20 | 21 | let getClientNameById = id => 22 | PromiseMaybeT(ClientRepository.getById(id)) //PromiseMaybeT< Promise< Maybe< Client>>>> 23 | .map(R.prop('name')) //Ramda lense equivalent to client=>client.name 24 | //PromiseMaybeT< Promise< Maybe< String>>>> 25 | .match({ //PromiseMaybeT< Promise< String>>> 26 | some: name => `client name:${name}`, 27 | none: () => `no client found` 28 | }) 29 | .unwrap(); //removes the PromiseMaybeT //Promise< String >> 30 | 31 | getClientNameById(1).then(console.log); 32 | getClientNameById(2).then(console.log); 33 | getClientNameById(3).then(console.log); 34 | 35 | 36 | } 37 | 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /functors/promise-maybe-composition-adapters/operators.js: -------------------------------------------------------------------------------- 1 | 2 | export var PromiseMaybeT = (promiseMaybe) =>({ 3 | map: (f) =>PromiseMaybeT(promiseMaybe.then(maybe=>maybe.map(f))) , 4 | match: (pattern)=>PromiseMaybeT(promiseMaybe.then(maybe=>maybe.match(pattern))), 5 | unwrap:()=>promiseMaybe 6 | }); 7 | 8 | -------------------------------------------------------------------------------- /functors/promise-maybe-composition/data/Repository.js: -------------------------------------------------------------------------------- 1 | 2 | import { firstOrNone } from "../../../common/firstOrNone.js" 3 | import fetch from 'node-fetch' 4 | 5 | export var ClientRepository = ({ 6 | getById: (id) => // returns a Promise> Type 7 | fetch("https://run.mocky.io/v3/a1678fa6-d99f-4502-939e-44dcd1479f9d") 8 | .then(response => response.json()) 9 | .then(data => data.clients.filter(client => client.id == id)) 10 | .then(firstOrNone) 11 | 12 | }); 13 | 14 | -------------------------------------------------------------------------------- /functors/promise-maybe-composition/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | import { ClientRepository } from "./data/Repository.js" 3 | import { mapT, matchT } from "./operators.js" 4 | import R from 'Ramda' 5 | 6 | export function demo() { 7 | 8 | { //initial 9 | ClientRepository.getById(1) 10 | .then(maybeClient => maybeClient.map(client => client.name)) 11 | .then( 12 | client => client.match({ 13 | some: name => console.log(`client name:${name}`), 14 | none: () => console.log(`no client found`) 15 | }) 16 | ) 17 | .catch(e => console.log(`fetch operation failed ${e}`)) 18 | } 19 | 20 | {//refactored using map,match operations 21 | 22 | 23 | ClientRepository.getById(1) 24 | .then(mapT(R.prop(`name`))) 25 | .then(matchT({ 26 | some: name => console.log(`client name:${name}`), 27 | none: () => console.log(`no client found`) 28 | })) 29 | .catch(e => console.log(`fetch operation failed ${e}`))//we will remove t his part when we talk about monads, 30 | //Since it is logical to merge promise and maybe since there is a transformation from maybe to a Promise . 31 | // In this way there are a Promise in a Promise 32 | 33 | 34 | } 35 | 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /functors/promise-maybe-composition/operators.js: -------------------------------------------------------------------------------- 1 | export let mapT = function (f) { 2 | return maybe => maybe.map(f) 3 | } 4 | 5 | export let matchT = function (pattern) { 6 | return maybe => maybe.match(pattern) 7 | } 8 | -------------------------------------------------------------------------------- /functors/reader-functor/demo.js: -------------------------------------------------------------------------------- 1 | import { Reader } from "../Reader.js" 2 | 3 | export function demo() { 4 | { 5 | //const titleViewTemplate = Reader(({ firstName, lastName }) => `
${firstName} - ${lastName}
`) 6 | 7 | var titleView = Reader(({ firstName, lastName }) => `
${firstName} - ${lastName}
`) 8 | .run({ firstName: "dimitris", lastName: "papadim" }); 9 | 10 | console.log(titleView) 11 | 12 | } 13 | 14 | { 15 | var decoratedWithSpan = 16 | Reader(({ firstName, lastName }) => `
${firstName} - ${lastName}
`) 17 | .map(x => ` this is a span${x}`) 18 | .run({ firstName: "dimitris", lastName: "papadim" }); 19 | 20 | console.log(decoratedWithSpan) 21 | } 22 | 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /functors/try-functor-sanctuary/data/Db.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../../model/Client.js" 2 | 3 | export let Db = { //mock client Db as Singleton 4 | Clients: [ 5 | new Client(1, "jim"), 6 | new Client(2, "jane") 7 | ] 8 | }; -------------------------------------------------------------------------------- /functors/try-functor-sanctuary/data/Repository.js: -------------------------------------------------------------------------------- 1 | import { Db } from "./Db.js" 2 | 3 | export let ClientRepository = { // a repository that throws an exception . Traditional imperative thinking 4 | getById: id => { 5 | var client = Db.Clients.filter(client => client.id == id); 6 | if (client && client.length > 0) return client[0]; 7 | else throw new Error(`no client found`); 8 | } 9 | }; -------------------------------------------------------------------------------- /functors/try-functor-sanctuary/demo.js: -------------------------------------------------------------------------------- 1 | 2 | import { ClientRepository } from "./data/Repository.js" 3 | import S from "sanctuary"; 4 | 5 | 6 | export function demo() { 7 | 8 | { 9 | console.log("Using Either curried composition"); 10 | // composing functions 11 | var getClientNameById = id => 12 | S.either(e => `error : ${e}`) 13 | (result => `client name:${result}`)( 14 | S.map(c => c.name) 15 | (S.encase(ClientRepository.getById)(id)) 16 | ); 17 | 18 | console.log(getClientNameById(1)); 19 | console.log(getClientNameById(2)); 20 | console.log(getClientNameById(3)); 21 | } 22 | 23 | { 24 | console.log("Using pipe"); 25 | //using pipe 26 | var getClientNameById = 27 | S.pipe([ 28 | S.encase(ClientRepository.getById), //because S.encase(ClientRepository.getById)(id) the id is moved as the lamdba id => outside of S.pipe 29 | S.map(S.prop(`name`)), 30 | S.either(e => `error : ${e}`) 31 | (result => `client name:${result}`) 32 | ]); 33 | 34 | console.log(getClientNameById(1)); 35 | console.log(getClientNameById(2)); 36 | console.log(getClientNameById(3)); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /monads/Either.js: -------------------------------------------------------------------------------- 1 | export let Right = (v) => ({ 2 | map: (f) => Right(f(v)), 3 | match: (pattern) => pattern.right(v), 4 | bind: f => f(v), 5 | }); 6 | 7 | export let Left = (v) => ({ 8 | map: (f) => Left(v), // we discard the f and just pass the value v along 9 | match: (pattern) => pattern.left(v), 10 | bind: f => Left(v), 11 | }); -------------------------------------------------------------------------------- /monads/IO.js: -------------------------------------------------------------------------------- 1 | export let IO = fn => ({ 2 | map: f => IO(() => f(fn())), 3 | bind: f => IO(() => f(fn()).run()), 4 | run: () => fn(), 5 | match: pattern => pattern(fn()) 6 | }); 7 | 8 | 9 | IO.of = v => IO(() => v); //pointed functor -------------------------------------------------------------------------------- /monads/Id.js: -------------------------------------------------------------------------------- 1 | export const Id = (v) => ({ 2 | map: f => Id(f(v)), 3 | bind: f => f(v), 4 | getValue: () => v, 5 | match: pattern => pattern(v) 6 | }); 7 | 8 | Id.of = v => Id(v); //pointed functor -------------------------------------------------------------------------------- /monads/Maybe.js: -------------------------------------------------------------------------------- 1 | export let Some = (v) => ({ 2 | map: f => Some(f(v)), 3 | match: pattern => pattern.some(v), 4 | bind: f => f(v), 5 | }); 6 | 7 | export let None = () => ({ 8 | map: _ => None(), 9 | match: pattern => pattern.none(), 10 | bind: _ => None(), 11 | }); 12 | -------------------------------------------------------------------------------- /monads/Reader.js: -------------------------------------------------------------------------------- 1 | export let Reader = (expr) => ({ 2 | map: f => Reader(env => f(Reader(expr).run(env))), 3 | bind: f => Reader(env => f(expr(env)).run(env)), 4 | run: env => expr(env), 5 | }); 6 | -------------------------------------------------------------------------------- /monads/State.js: -------------------------------------------------------------------------------- 1 | export var State = expression => ({ 2 | map: f => 3 | State(previousState => { 4 | var { value, state } = expression(previousState); 5 | return { value: f(value), state: state }; 6 | }), 7 | bind: f => 8 | State(previousState => { 9 | var { value, state } = expression(previousState); 10 | var newState = f(value).run(state); 11 | return newState; 12 | }), 13 | run: previousState => expression(previousState) 14 | }); 15 | 16 | State.of = v => State((s) => ({ value: v, state: s })); //pointed functor -------------------------------------------------------------------------------- /monads/array-monad/demo.js: -------------------------------------------------------------------------------- 1 | import R from 'ramda' 2 | import S from 'sanctuary' 3 | 4 | export function demo() { 5 | 6 | var array = [2, 3, 4, 5]; 7 | var mutipleOperations = x => [x * x, x * x * x]; 8 | //native array flatMap 9 | { 10 | // map 11 | let result1 = array.map(mutipleOperations); 12 | console.log(JSON.stringify(result1)); 13 | //bind 14 | let result2 = array.flatMap(mutipleOperations); 15 | console.log(JSON.stringify(result2)); 16 | } 17 | 18 | //ramda 19 | { 20 | let flatMap = f => R.compose(R.flatten, R.map(f)) 21 | let result = flatMap(mutipleOperations)(array); 22 | console.log(JSON.stringify(result)); 23 | } 24 | 25 | //sanctuary 26 | { 27 | let flatMap = f => S.pipe([S.map(f), S.join]); 28 | let result = flatMap(mutipleOperations)(array); 29 | console.log(JSON.stringify(result)); 30 | //or using the S.chain on the array this is the standar monadic bind a.k.a flatMap 31 | 32 | let result1 = S.chain(mutipleOperations)(array); 33 | console.log(JSON.stringify(result1)); 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /monads/composeK.js: -------------------------------------------------------------------------------- 1 | export let composeK = (f, g) => a => f(a).bind(g); 2 | 3 | export let composeP = (f, g) => a => f(a).then(g); -------------------------------------------------------------------------------- /monads/either-monad-example/data/Repositories.js: -------------------------------------------------------------------------------- 1 | 2 | import { firstOrNone } from "../../../common/firstOrNone.js" 3 | import { maybeToEither } from "../../../common/maybeToEither.js" 4 | import { Db } from "../../model/Db.js" 5 | 6 | export let ClientRepository = ({ 7 | getById: (id) => 8 | maybeToEither(`no client found`) 9 | (firstOrNone(Db.Clients.filter(client => client.id == id))) 10 | 11 | }); 12 | 13 | export let EmployeeRepository = ({ 14 | getById: (id) => 15 | maybeToEither(`no employee found`) 16 | (firstOrNone(Db.Employees.filter(employee => employee.id == id))) 17 | 18 | }); -------------------------------------------------------------------------------- /monads/either-monad-example/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | import { Employee } from "../model/Employee.js" 3 | import { ClientRepository, EmployeeRepository } from "./data/Repositories.js" 4 | 5 | 6 | export function demo() { 7 | 8 | let ClientService = { 9 | getEmployeeNameByClientId: clientId => 10 | ClientRepository.getById(clientId) 11 | .map(Client.employeeId) 12 | .bind(EmployeeRepository.getById) 13 | .map(Employee.name) 14 | .match({ 15 | right: value => `employee name: ${value}`, 16 | left: error => `error: ${error}` 17 | }) 18 | }; 19 | 20 | console.log(ClientService.getEmployeeNameByClientId(1)); 21 | console.log(ClientService.getEmployeeNameByClientId(2)); 22 | console.log(ClientService.getEmployeeNameByClientId(3)); 23 | } 24 | -------------------------------------------------------------------------------- /monads/either-monad/demo.js: -------------------------------------------------------------------------------- 1 | import { Left, Right } from "../Either.js" 2 | 3 | export function demo() { 4 | 5 | //Right.bind(Left)==Left 6 | Right(3) 7 | .bind(x => Left("error2")) 8 | .match({ 9 | right: v => console.log(`Right:${v}`), 10 | left: (error) => console.log(`Left ${error}`) 11 | }) 12 | 13 | //Left.bind(Right)==Left 14 | Left("error1") 15 | .bind(x => Right(5 + x)) 16 | .match({ 17 | right: v => console.log(`Right:${v}`), 18 | left: (error) => console.log(`Left ${error}`) 19 | }) 20 | 21 | //Left.bind(Left)==Left 22 | Left("error1") 23 | .bind(x => Left("error2")) 24 | .match({ 25 | right: v => console.log(`Right:${v}`), 26 | left: (error) => console.log(`Left ${error}`) 27 | }) 28 | 29 | //Right.bind(Right)==Right 30 | Right(3) 31 | .bind(x => Right(5 + x)) 32 | .match({ 33 | right: v => console.log(`Right:${v}`), 34 | left: (error) => console.log(`Left ${error}`) 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /monads/fluture-monad-example/data/Repositories.js: -------------------------------------------------------------------------------- 1 | 2 | import { firstOrNone } from "../../../common/firstOrNone.js" 3 | import { maybeToPromise } from "../../../common/maybeToPromise.js" 4 | import { Db } from "../../model/Db.js" 5 | 6 | export let ClientRepository = ({ 7 | getById: (id) => 8 | maybeToPromise(`no client found`) 9 | (firstOrNone(Db.Clients.filter(client => client.id == id))) 10 | 11 | 12 | }); 13 | 14 | export let EmployeeRepository = ({ 15 | getById: (id) => 16 | maybeToPromise(`no employee found`) 17 | (firstOrNone(Db.Employees.filter(employee => employee.id == id))) 18 | 19 | 20 | }); -------------------------------------------------------------------------------- /monads/fluture-monad-example/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | import { Employee } from "../model/Employee.js" 3 | import { ClientRepository, EmployeeRepository } from "./data/Repositories.js" 4 | import { chain, map, fork, encaseP } from "fluture"; 5 | 6 | // encaseP is used to convert from Promise to a Fluture https://github.com/fluture-js/Fluture#encasep 7 | 8 | export function demo() { 9 | 10 | let ClientService = { 11 | getEmployeeNameByClientId: clientId => 12 | encaseP(ClientRepository.getById)(clientId) // Future 13 | .pipe(map(Client.employeeId)) // Future 14 | .pipe(chain(encaseP(EmployeeRepository.getById))) // Future 15 | .pipe(map(Employee.name)) 16 | .pipe(fork // Future 17 | (x => console.log("error:" + x)) 18 | (x => console.log(x.name)) 19 | ) 20 | }; 21 | 22 | ClientService.getEmployeeNameByClientId(1); 23 | ClientService.getEmployeeNameByClientId(2); 24 | ClientService.getEmployeeNameByClientId(3); 25 | } 26 | -------------------------------------------------------------------------------- /monads/foldM-example/data/Repositories.js: -------------------------------------------------------------------------------- 1 | 2 | import { firstOrNone } from "../../../common/firstOrNone.js" 3 | import { Db } from "../../model/Db.js" 4 | 5 | export let ClientRepository = ({ 6 | getById: (id) => 7 | firstOrNone(Db.Clients.filter(client => client.id == id)) 8 | 9 | }); 10 | 11 | export let EmployeeRepository = ({ 12 | getById: (id) => 13 | firstOrNone(Db.Employees.filter(employee => employee.id == id)) 14 | 15 | }); -------------------------------------------------------------------------------- /monads/foldM-example/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | import { Employee } from "../model/Employee.js" 3 | import { ClientRepository, EmployeeRepository } from "./data/Repositories.js" 4 | import { composeK } from "../composeK.js" 5 | import R from 'ramda' 6 | 7 | export function demo() { 8 | 9 | var maybeEmployeeIdByClientId = clientId => 10 | ClientRepository 11 | .getById(clientId) 12 | .map(R.prop(`employeeId`)); //number=>Maybe 13 | 14 | var getEmployeeNameById = employeeId => 15 | EmployeeRepository 16 | .getById(employeeId) 17 | .map(R.prop(`name`)); //number=>Maybe 18 | 19 | const composeK = R.composeWith((f, res) => res = res.bind(f)); 20 | 21 | var employeeNameByClientId = composeK([ 22 | maybeEmployeeIdByClientId, 23 | getEmployeeNameById] 24 | ); 25 | 26 | var getNameOrError = maybe => //fold the maybe to get a string 27 | maybe.match({ 28 | some: value => `employee name: ${value}`, 29 | none: _ => `could not get employee name ` 30 | }); 31 | 32 | [1, 2, 3, 4] // a list of ClientIds 33 | .map(employeeNameByClientId) 34 | .map(getNameOrError) 35 | .forEach(console.log); 36 | 37 | // log(matchMaybe(employeeNameByClientId(1))); 38 | // log(matchMaybe(employeeNameByClientId(2))); 39 | // log(matchMaybe(employeeNameByClientId(3))); 40 | // log(matchMaybe(employeeNameByClientId(4))); 41 | } 42 | -------------------------------------------------------------------------------- /monads/foldM/demo.js: -------------------------------------------------------------------------------- 1 | import { Id } from "../Id.js" 2 | 3 | //https://stackblitz.com/edit/typescript-foldm-acn4wp?file=index.ts 4 | 5 | export function demo() { 6 | 7 | var array = [2, 4, 5, 6]; 8 | 9 | var accumulation = new Id(0); 10 | for (let i = 0; i < array.length; i++) { 11 | const element = array[i]; 12 | accumulation = accumulation.bind(acc => new Id(element + acc)); 13 | } 14 | log(accumulation); 15 | } 16 | 17 | -------------------------------------------------------------------------------- /monads/identity-example/demo.js: -------------------------------------------------------------------------------- 1 | 2 | //this type only purpose is to store the value v in order to witness the mechanics 3 | const Id = (v) => ({ 4 | v: v, 5 | map: f => Id(f(v)), 6 | bind: f => f(v), 7 | }); 8 | 9 | 10 | export function demo() { 11 | 12 | // map 13 | let result1 = Id(5).map(x => Id(x + 2)); 14 | console.log(JSON.stringify(result1)); 15 | 16 | //bind 17 | let result2 = Id(5).bind(x => Id(x + 2)); 18 | console.log(JSON.stringify(result2)); 19 | } 20 | -------------------------------------------------------------------------------- /monads/kleisli-composition/composeK.js: -------------------------------------------------------------------------------- 1 | export let composeK = (f, g) => a => f(a).bind(g); 2 | 3 | export let composeP = (f, g) => a => f(a).then(g); -------------------------------------------------------------------------------- /monads/kleisli-composition/demo.js: -------------------------------------------------------------------------------- 1 | import R from 'ramda' 2 | import S from 'sanctuary' 3 | import { composeK } from "./composeK.js" 4 | import { Id } from "../Id.js" 5 | import { Db } from '../model/Db.js'; 6 | export function demo() { 7 | 8 | { 9 | var f1 = x => Id(x * x); 10 | var f2 = x => Id(2 * x); 11 | 12 | var composeK = (f, g) => a => f(a).map(b => g(b)).bind(x => x); 13 | 14 | console.log(composeK(f1, f2)(4).getValue()); //32 15 | } 16 | 17 | { 18 | var getClients = clientId => Db.Clients.filter(client => client.id == clientId); 19 | var getEmployees = employeeId => Db.Employees.filter(employee => employee.id == employeeId); 20 | 21 | let getEmployeeNameByClientId = id => { 22 | //let employee = ; 23 | let name = getClients(id) 24 | .map(client => client.employeeId) 25 | .flatMap(getEmployees); 26 | 27 | return name; 28 | } 29 | 30 | //let optional = (o, f) => o != null ? f(o) : null; 31 | // let getEmployeeNameByClientId = id => { 32 | // //let employee = ; 33 | // let name = optional(getEmployee(optional(getClients(id), x => x.employeeId)), x => x.name); 34 | // return name; 35 | // } 36 | 37 | console.log(getEmployeeNameByClientId(1)) 38 | } 39 | 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /monads/kleisli-fold/demo.js: -------------------------------------------------------------------------------- 1 | import { Some, None } from "../Maybe.js" 2 | //https://stackblitz.com/edit/id-kleisli-fold?file=index.js 3 | 4 | export function demo() { 5 | 6 | var composeK = (f, g) => a => f(a).bind(g); 7 | var foldK = maybesK => maybesK.reduce((comp, f) => composeK(comp, f), x => Some(x)); 8 | 9 | //validations in kleisli x->Maybe<{name,age}> form 10 | var validationsK = [ 11 | x => (x.name.length > 0 ? Some(x) : None()), //some validation for the name 12 | x => (x.age > 25 ? Some(x) : None()) //some validation for the age 13 | ]; 14 | 15 | var overallValidationK = foldK(validationsK); 16 | 17 | 18 | 19 | 20 | var validationResult = overallValidationK({ name: "jane", age: 25 }).match({ 21 | some: x => `employee: ${x.name} is valid`, 22 | none: _ => `could not validate employee` 23 | }); 24 | 25 | console.log(validationResult); 26 | 27 | { 28 | var validationResult = overallValidationK({ name: "jane", age: 30 }).match({ 29 | some: x => `employee: ${x.name} is valid`, 30 | none: _ => `could not validate employee` 31 | }); 32 | 33 | console.log(validationResult); 34 | } 35 | 36 | 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /monads/maybe-kleisli-composition-ramda/data/Repositories.js: -------------------------------------------------------------------------------- 1 | 2 | import { firstOrNone } from "../../../common/firstOrNone.js" 3 | import { Db } from "../../model/Db.js" 4 | 5 | export let ClientRepository = ({ 6 | getById: (id) => 7 | firstOrNone(Db.Clients.filter(client => client.id == id)) 8 | 9 | }); 10 | 11 | export let EmployeeRepository = ({ 12 | getById: (id) => 13 | firstOrNone(Db.Employees.filter(employee => employee.id == id)) 14 | 15 | }); -------------------------------------------------------------------------------- /monads/maybe-kleisli-composition-ramda/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | import { Employee } from "../model/Employee.js" 3 | import { ClientRepository, EmployeeRepository } from "./data/Repositories.js" 4 | import { composeK } from "../composeK.js" 5 | import R from 'ramda' 6 | 7 | export function demo() { 8 | 9 | var maybeEmployeeIdByClientId = clientId => 10 | ClientRepository 11 | .getById(clientId) 12 | .map(R.prop(`employeeId`)); //number=>Maybe 13 | 14 | var getEmployeeNameById = employeeId => 15 | EmployeeRepository 16 | .getById(employeeId) 17 | .map(R.prop(`name`)); //number=>Maybe 18 | 19 | const composeK = R.composeWith((f, res) => res = res.bind(f)); 20 | 21 | var employeeNameByClientId = composeK([ 22 | maybeEmployeeIdByClientId, 23 | getEmployeeNameById] 24 | ); 25 | 26 | var getNameOrError = maybe => //fold the maybe to get a string 27 | maybe.match({ 28 | some: value => `employee name: ${value}`, 29 | none: _ => `could not get employee name ` 30 | }); 31 | 32 | [1, 2, 3, 4] // a list of ClientIds 33 | .map(employeeNameByClientId) 34 | .map(getNameOrError) 35 | .forEach(console.log); 36 | 37 | // log(matchMaybe(employeeNameByClientId(1))); 38 | // log(matchMaybe(employeeNameByClientId(2))); 39 | // log(matchMaybe(employeeNameByClientId(3))); 40 | // log(matchMaybe(employeeNameByClientId(4))); 41 | } 42 | -------------------------------------------------------------------------------- /monads/maybe-kleisli-composition/data/Repositories.js: -------------------------------------------------------------------------------- 1 | 2 | import { firstOrNone } from "../../../common/firstOrNone.js" 3 | import { Db } from "../../model/Db.js" 4 | 5 | export let ClientRepository = ({ 6 | getById: (id) => 7 | firstOrNone(Db.Clients.filter(client => client.id == id)) 8 | 9 | }); 10 | 11 | export let EmployeeRepository = ({ 12 | getById: (id) => 13 | firstOrNone(Db.Employees.filter(employee => employee.id == id)) 14 | 15 | }); -------------------------------------------------------------------------------- /monads/maybe-kleisli-composition/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | import { Employee } from "../model/Employee.js" 3 | import { ClientRepository, EmployeeRepository } from "./data/Repositories.js" 4 | import { composeK } from "../composeK.js" 5 | 6 | export function demo() { 7 | 8 | var maybeEmployeeIdByClientId = clientId => 9 | ClientRepository 10 | .getById(clientId) 11 | .map(Client.employeeId); //number=>Maybe 12 | 13 | var getEmployeeNameById = employeeId => 14 | EmployeeRepository 15 | .getById(employeeId) 16 | .map(Employee.name); //number=>Maybe 17 | 18 | var employeeNameByClientId = composeK( 19 | maybeEmployeeIdByClientId, 20 | getEmployeeNameById 21 | ); 22 | 23 | var getNameOrError = maybe => //fold the maybe to get a string 24 | maybe.match({ 25 | some: value => `employee name: ${value}`, 26 | none: _ => `could not get employee name ` 27 | }); 28 | 29 | [1, 2, 3, 4] // a list of ClientIds 30 | .map(employeeNameByClientId) 31 | .map(getNameOrError) 32 | .forEach(console.log); 33 | 34 | // log(matchMaybe(employeeNameByClientId(1))); 35 | // log(matchMaybe(employeeNameByClientId(2))); 36 | // log(matchMaybe(employeeNameByClientId(3))); 37 | // log(matchMaybe(employeeNameByClientId(4))); 38 | } 39 | -------------------------------------------------------------------------------- /monads/maybe-monad-example/data/Repositories.js: -------------------------------------------------------------------------------- 1 | 2 | import { firstOrNone } from "../../../common/firstOrNone.js" 3 | import { Db } from "../../model/Db.js" 4 | 5 | export let ClientRepository = ({ 6 | getById: (id) => 7 | firstOrNone(Db.Clients.filter(client => client.id == id)) 8 | 9 | }); 10 | 11 | export let EmployeeRepository = ({ 12 | getById: (id) => 13 | firstOrNone(Db.Employees.filter(employee => employee.id == id)) 14 | 15 | }); -------------------------------------------------------------------------------- /monads/maybe-monad-example/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | import { Employee } from "../model/Employee.js" 3 | import { ClientRepository, EmployeeRepository } from "./data/Repositories.js" 4 | 5 | 6 | export function demo() { 7 | 8 | let ClientService = { 9 | getEmployeeNameByClientId: clientId => 10 | ClientRepository.getById(clientId) // Maybe 11 | .map(Client.employeeId) // Maybe 12 | .bind(EmployeeRepository.getById) // Maybe 13 | .map(Employee.name) // Maybe 14 | .match({ // String 15 | some: value => `employee name: ${value}`, 16 | none: _ => `Error: could not get employee name ` 17 | }) 18 | }; 19 | 20 | console.log(ClientService.getEmployeeNameByClientId(1)); 21 | console.log(ClientService.getEmployeeNameByClientId(2)); 22 | console.log(ClientService.getEmployeeNameByClientId(3)); 23 | } 24 | -------------------------------------------------------------------------------- /monads/maybe-monad/demo.js: -------------------------------------------------------------------------------- 1 | import { Some, None } from "../Maybe.js" 2 | 3 | export function demo() { 4 | 5 | //some.bind(None)==None 6 | Some(3) 7 | .bind(x => None()) 8 | .match({ 9 | some: v => console.log(`Some:${v}`), 10 | none: () => console.log(`None`) 11 | }) 12 | 13 | //None.bind(some)==None 14 | None() 15 | .bind(x => Some(5 + x)) 16 | .match({ 17 | some: v => console.log(`Some:${v}`), 18 | none: () => console.log(`None`) 19 | }) 20 | 21 | //None.bind(None)==None 22 | None() 23 | .bind(x => None()) 24 | .match({ 25 | some: v => console.log(`Some:${v}`), 26 | none: () => console.log(`None`) 27 | }) 28 | 29 | //Some.bind(Some)==Some 30 | Some(3) 31 | .bind(x => Some(5 + x)) 32 | .match({ 33 | some: v => console.log(`Some:${v}`), 34 | none: () => console.log(`None`) 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /monads/model/Client.js: -------------------------------------------------------------------------------- 1 | export class Client { 2 | constructor(id, name, employeeId) { 3 | this.id = id; 4 | this._name = name; 5 | this._employeeId = employeeId; 6 | } 7 | get name() { 8 | //getter and setters : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get 9 | return this._name; 10 | } 11 | get employeeId() { 12 | //getter and setters : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get 13 | return this._employeeId; 14 | } 15 | set name(x) { 16 | this._name = x; 17 | } 18 | 19 | //static lenses 20 | static get name() { 21 | return c => c._name; 22 | } 23 | 24 | static get employeeId() { 25 | return c => c.employeeId; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /monads/model/Db.js: -------------------------------------------------------------------------------- 1 | import { Client } from "./Client.js" 2 | import { Employee } from "./Employee.js" 3 | 4 | 5 | export let Db = { 6 | Clients: 7 | [new Client(1, "jim", 2), 8 | new Client(2, "jane", 3)], 9 | 10 | Employees: 11 | [new Employee(1, "jack"), 12 | new Employee(2, "jill")], 13 | 14 | } 15 | -------------------------------------------------------------------------------- /monads/model/Employee.js: -------------------------------------------------------------------------------- 1 | export class Employee { 2 | constructor(id, name) { 3 | this.id = id; 4 | this.name = name; 5 | } 6 | //static lenses 7 | static get name() { 8 | return c => c.name; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /monads/promise-maybe-monad-combination-example/PromiseMaybeT.js: -------------------------------------------------------------------------------- 1 | 2 | export var PromiseMaybeT = (promiseMaybe) => ({ 3 | map: f => PromiseMaybeT(promiseMaybe.then(maybe => maybe.map(f))), 4 | match: pattern => PromiseMaybeT(promiseMaybe.then(maybe => maybe.match(pattern))), 5 | bind: f => PromiseMaybeT(promiseMaybe.then(maybe => maybe.bind(f))), 6 | //this returns the internal Promise 7 | unwrap: () => promiseMaybe 8 | }); -------------------------------------------------------------------------------- /monads/promise-maybe-monad-combination-example/data/Repositories.js: -------------------------------------------------------------------------------- 1 | 2 | import { firstOrNone } from "../../../common/firstOrNone.js" 3 | import { maybeToEither } from "../../../common/maybeToEither.js" 4 | import fetch from 'node-fetch' 5 | 6 | export var ClientRepository = ({ 7 | getById: (id) => 8 | fetch("https://run.mocky.io/v3/cb2b55a6-803c-40c5-8c04-2a96155a989f") 9 | .then(response => response.json()) 10 | .then(data => data.clients.filter(client => client.id == id)) 11 | .then(firstOrNone) 12 | }); 13 | 14 | 15 | export var EmployeeRepository = ({ 16 | getById: (id) => 17 | fetch("https://run.mocky.io/v3/a1678fa6-d99f-4502-939e-44dcd1479f9d") 18 | .then(response => response.json()) 19 | .then(data => data.clients.filter(employee => employee.id == id)) 20 | .then(firstOrNone) 21 | }); 22 | -------------------------------------------------------------------------------- /monads/promise-maybe-monad-combination-example/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | import { Employee } from "../model/Employee.js" 3 | import { ClientRepository, EmployeeRepository } from "./data/Repositories.js" 4 | import { PromiseMaybeT } from "./PromiseMaybeT.js" 5 | 6 | export function demo() { 7 | 8 | let getEmployeeNameByClientId = clientId => //===============Type===================== 9 | PromiseMaybeT(ClientRepository.getById(clientId)) //PromiseMaybeT< Promise< Maybe< Client >>>> 10 | .map(Client.employeeId) //PromiseMaybeT< Promise< Maybe< Integer >>>> 11 | .bind(EmployeeRepository.getById) //PromiseMaybeT< Promise< Maybe< Employee >>>> 12 | .map(Employee.name) //PromiseMaybeT< Promise< Maybe< String >>>> 13 | .match({ //PromiseMaybeT< Promise< String >>> 14 | some: value => `employee name: ${value}`, 15 | none: _ => `nothing found` 16 | }) 17 | .unwrap(); // Promise< String>> 18 | 19 | 20 | getEmployeeNameByClientId(1).then(r => console.log(`id:1 ${r}`)); 21 | getEmployeeNameByClientId(2).then(r => console.log(`id:2 ${r}`)); 22 | getEmployeeNameByClientId(3).then(r => console.log(`id:3 ${r}`)); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /monads/promise-monad-example/data/Repositories.js: -------------------------------------------------------------------------------- 1 | 2 | import { firstOrNone } from "../../../common/firstOrNone.js" 3 | import { maybeToPromise } from "../../../common/maybeToPromise.js" 4 | import { Db } from "../../model/Db.js" 5 | 6 | export let ClientRepository = ({ 7 | getById: (id) => 8 | maybeToPromise(`no client found`) 9 | (firstOrNone(Db.Clients.filter(client => client.id == id))) 10 | 11 | 12 | }); 13 | 14 | export let EmployeeRepository = ({ 15 | getById: (id) => 16 | maybeToPromise(`no employee found`) 17 | (firstOrNone(Db.Employees.filter(employee => employee.id == id))) 18 | 19 | 20 | }); -------------------------------------------------------------------------------- /monads/promise-monad-example/demo.js: -------------------------------------------------------------------------------- 1 | import { Client } from "../model/Client.js" 2 | import { Employee } from "../model/Employee.js" 3 | import { ClientRepository, EmployeeRepository } from "./data/Repositories.js" 4 | 5 | 6 | export function demo() { 7 | 8 | let ClientService = ({ 9 | getEmployeeNameByClientId: clientId => 10 | ClientRepository.getById(clientId) // Promise 11 | .then(Client.employeeId) // Promise 12 | .then(EmployeeRepository.getById) // Promise 13 | .then(Employee.name) // Promise 14 | //the following are pattern matching 15 | .then(value => `employee name: ${value}`) // Promise 16 | .catch(error => `error: ${error}`) // Promise 17 | }); 18 | 19 | ClientService.getEmployeeNameByClientId(1).then(r => console.log(`id:1 ${r}`)); 20 | ClientService.getEmployeeNameByClientId(2).then(r => console.log(`id:2 ${r}`)); 21 | ClientService.getEmployeeNameByClientId(3).then(r => console.log(`id:3 ${r}`)); 22 | } 23 | -------------------------------------------------------------------------------- /monads/promise-monad/demo.js: -------------------------------------------------------------------------------- 1 | export function demo() { 2 | //resolve.bind(reject)==reject 3 | Promise.resolve(3) 4 | .then(x => Promise.reject("error2")) 5 | .then(v => console.log(`A-Promise.resolve:${v}`)) 6 | .catch(error => console.log(`A-Promise.reject:${error}`)); 7 | 8 | //reject.bind(resolve)==reject 9 | Promise.reject("error1") 10 | .then(x => Promise.resolve(5 + x)) 11 | .then(v => console.log(`B-Promise.resolve:${v}`)) 12 | .catch(error => console.log(`B-Promise.reject :${error}`)); 13 | 14 | //reject.bind(reject)==reject 15 | Promise.reject("error1") 16 | .then(x => Promise.reject("error2")) 17 | .then(v => console.log(`C-Promise.resolve:${v}`)) 18 | .catch(error => console.log(`C-Promise.reject :${error}`)); 19 | 20 | //resolve.bind(resolve)==resolve 21 | Promise.resolve(3) 22 | .then(x => Promise.resolve(5 + x)) 23 | .then(v => console.log(`D-Promise.resolve:${v}`)) 24 | .catch(error => console.log(`D-Promise.reject: ${error}`)); 25 | } 26 | -------------------------------------------------------------------------------- /monads/state-example-1/demo.js: -------------------------------------------------------------------------------- 1 | import { State } from "./../State.js" 2 | 3 | 4 | //https://stackblitz.com/edit/state-example-1?file=index.js 5 | // javascript implementation of the example - 1 of the State monad in hasklell 6 | //https://wiki.haskell.org/State_Monad#Complete_and_Concrete_Example_1 7 | 8 | export function demo() { 9 | 10 | Array.prototype.match = function (pattern) { 11 | if (this.length == 0) { 12 | return pattern.empty(); 13 | } else { 14 | return pattern.cons(this[0], this.slice(1)); 15 | } 16 | }; 17 | 18 | 19 | // type GameValue = Int 20 | // type GameState = (Bool, Int) 21 | 22 | // playGame :: String -> State GameState GameValue 23 | 24 | var playGame = wordSequence => wordSequence.match({ 25 | empty: () => { 26 | return State(s => s) 27 | },//terminates 28 | cons: (x, xs) => { 29 | return State(({ on, counter }) => { 30 | if (x == "a" && on) 31 | return ({ state: { on: on, counter: counter + 1 } }) 32 | else if (x == "b" && on) 33 | return ({ state: { on: on, counter: counter - 1 } }) 34 | else if (x == "c") 35 | return ({ state: { on: !on, counter: counter } }) 36 | else return s 37 | }).bind(_ => playGame(xs)) 38 | } 39 | 40 | }) 41 | 42 | 43 | var display = s => console.log(JSON.stringify(playGame(s).run({ on: true, counter: 0 }))) 44 | 45 | display(Array.from("aaaabc")) 46 | display(Array.from("aaaabbbbbbbc")) 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /monads/state-monad/demo.js: -------------------------------------------------------------------------------- 1 | import { State } from "../State.js" 2 | 3 | export function demo() { 4 | 5 | { 6 | var state = State(previousState => { 7 | var newStateValuePair = ({ 8 | value: "this is the value it can be anything", 9 | state: { a: false, counter: previousState.counter + 1 }// this is something that must have the same items as the state 10 | }) 11 | return newStateValuePair; 12 | }); 13 | 14 | var result = state.run({ a: true, counter: 0 }) 15 | console.log(JSON.stringify(result)); 16 | } 17 | 18 | 19 | { 20 | var state = State(({ on, counter }) => { 21 | return ({ value: "", state: { on: on, counter: counter + 1 } }) //we ignore the value and work only with the state 22 | }).bind(_ => State(({ on, counter }) => { //we ignore the value and work only with the state 23 | return ({ value: "", state: { on: on, counter: counter + 1 } }) //we ignore the value and work only with the state 24 | })); 25 | 26 | var result = state.run({ on: true, counter: 0 }); 27 | console.log(JSON.stringify(result)); 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /monads/state-transaction-portofolio-example-crocks/demo.js: -------------------------------------------------------------------------------- 1 | 2 | import { sell, buy } from "./transactions.js"; 3 | 4 | export function demo() { 5 | 6 | var protfolio = {}; 7 | protfolio["AKL"] = 7; 8 | protfolio["MSR"] = 4; 9 | 10 | var prices = {}; 11 | prices["AKL"] = 100; 12 | prices["MSR"] = 20; 13 | 14 | var moveTransaction = sell("AKL", 3) 15 | .chain(buy("MSR")) 16 | .runWith({ protfolio: protfolio, prices: prices }); 17 | 18 | console.log(JSON.stringify(moveTransaction.toArray())); 19 | 20 | 21 | } 22 | 23 | -------------------------------------------------------------------------------- /monads/state-transaction-portofolio-example-crocks/transactions.js: -------------------------------------------------------------------------------- 1 | import crocks from "crocks" 2 | 3 | const { State,Pair } = crocks 4 | 5 | export let sell = (stockName, numberOfStocks) => 6 | State(({ protfolio, prices }) => { 7 | //we pass along the prices also in the state 8 | var revenue = numberOfStocks * prices[stockName]; 9 | protfolio[stockName] = protfolio[stockName] - numberOfStocks; 10 | var newProtfolio = protfolio; 11 | 12 | return Pair(revenue, { protfolio: newProtfolio, prices: prices }); 13 | }); 14 | 15 | export let buy = stockName => valueOfStocks => 16 | State(({ protfolio, prices }) => { 17 | var numberOfStocks = Math.round(valueOfStocks / prices[stockName]); 18 | var residualMoney = valueOfStocks - numberOfStocks * prices[stockName]; 19 | 20 | protfolio[stockName] = protfolio[stockName] + numberOfStocks; 21 | var newProtfolio = protfolio; 22 | 23 | return Pair(residualMoney, { protfolio: newProtfolio, prices: prices }); 24 | }); 25 | -------------------------------------------------------------------------------- /monads/state-transaction-portofolio-example/demo.js: -------------------------------------------------------------------------------- 1 | 2 | import { sell, buy } from "./transactions.js"; 3 | 4 | export function demo() { 5 | 6 | var protfolio = {}; 7 | protfolio["AKL"] = 7; 8 | protfolio["MSR"] = 4; 9 | 10 | var prices = {}; 11 | prices["AKL"] = 100; 12 | prices["MSR"] = 20; 13 | 14 | var moveTransaction = sell("AKL", 3) 15 | .bind(buy("MSR")) 16 | .run({ protfolio: protfolio, prices: prices }); 17 | console.log(JSON.stringify(moveTransaction.state)); 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /monads/state-transaction-portofolio-example/transactions.js: -------------------------------------------------------------------------------- 1 | import { State } from "../State.js" 2 | 3 | export let sell = (stockName, numberOfStocks) => 4 | State(({ protfolio, prices }) => { 5 | var revenue = numberOfStocks * prices[stockName]; 6 | protfolio[stockName] = protfolio[stockName] - numberOfStocks; 7 | var newProtfolio = protfolio; 8 | 9 | return { 10 | value: revenue, 11 | state: { protfolio: newProtfolio, prices: prices } 12 | }; 13 | }); 14 | 15 | export let buy = stockName => valueOfStocks => 16 | State(({ protfolio, prices }) => { 17 | var numberOfStocks = Math.round(valueOfStocks / prices[stockName]); 18 | var residualMoney = valueOfStocks - numberOfStocks * prices[stockName]; 19 | 20 | protfolio[stockName] = protfolio[stockName] + numberOfStocks; 21 | var newProtfolio = protfolio; 22 | 23 | return { 24 | value: residualMoney, 25 | state: { protfolio: newProtfolio, prices: prices } 26 | }; 27 | }); 28 | -------------------------------------------------------------------------------- /monoids/1.monoid.demo.js: -------------------------------------------------------------------------------- 1 | 2 | export function demo() { 3 | var Sum = { empty: () => 0, concat: (x, y) => x + y } 4 | let arraySum = [1, 2, 3, 4, 5, 6].reduce(Sum.concat, Sum.empty()); 5 | console.log(arraySum); 6 | 7 | var Product = { empty: () => 1, concat: (x, y) => x * y } 8 | let arrayProduct = [1, 2, 3, 4, 5, 6].reduce(Product.concat, Product.empty()); 9 | console.log(arrayProduct); 10 | } 11 | -------------------------------------------------------------------------------- /monoids/2.monoid.fluid.demo.js: -------------------------------------------------------------------------------- 1 | 2 | export function demo() { 3 | class Sum { 4 | constructor(v) { this.value = v; } 5 | static empty() { return new Sum(0); } 6 | concat(b) { return new Sum(this.value + b.value); } 7 | }; 8 | 9 | var arraySum = [1, 2, 3, 4, 5, 6] 10 | .map(x => new Sum(x)) 11 | .reduce((a, b) => a.concat(b), Sum.empty()); 12 | 13 | console.log(arraySum) 14 | } 15 | -------------------------------------------------------------------------------- /monoids/decorator.pattern/demo.js: -------------------------------------------------------------------------------- 1 | import R from 'ramda'; 2 | 3 | export function demo() { 4 | 5 | var createProduct = price => ({ price: () => price }); 6 | 7 | //decorators 8 | var addPackaging = product => ({ price: () => product.price() + 0.5 }); 9 | var addRibons = product => ({ price: () => product.price() + 0.3 }); 10 | var addLayeredPackaging = numberOfLayers => product => ({ 11 | price: () => product.price() + 0.5 * numberOfLayers 12 | }); 13 | 14 | //addRibons(addPackaging(createProduct(20))); 15 | 16 | { 17 | var finalProduct = R.compose( 18 | addRibons, 19 | addLayeredPackaging(3), 20 | addPackaging, 21 | createProduct 22 | )(20); 23 | 24 | console.log("using R.compose"); 25 | console.log(finalProduct.price()); 26 | } 27 | 28 | //using R.pipe the order of the functions is reversed 29 | { 30 | var finalProduct = R.pipe( 31 | createProduct, 32 | addLayeredPackaging(3), 33 | addPackaging, 34 | addRibons 35 | )(20); 36 | 37 | console.log("using R.pipe"); 38 | console.log(finalProduct.price()); 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /monoids/hashmap-monoid/HashMapMonoid.js: -------------------------------------------------------------------------------- 1 | 2 | export var HashMapMonoid = valueMonoid => ({ 3 | empty: {}, 4 | concat: (x, y) => { 5 | var merge = Object.assign({}, y); 6 | for (var word in x) { 7 | if (merge[word]) { merge[word] = valueMonoid.concat(merge[word], (x[word])); } 8 | else { 9 | merge[word] = merge[word] = x[word]; 10 | } 11 | } 12 | return merge; 13 | } 14 | }) -------------------------------------------------------------------------------- /monoids/hashmap-monoid/demo.js: -------------------------------------------------------------------------------- 1 | 2 | import { HashMapMonoid } from "./HashMapMonoid.js"; 3 | export function demo() { 4 | 5 | var dictionary1 = {}; 6 | dictionary1["and"] = 5; 7 | dictionary1["the"] = 3; 8 | 9 | var dictionary2 = {}; 10 | dictionary2["and"] = 7; 11 | dictionary2["or"] = 4; 12 | 13 | var Sum = { empty: 0, concat: (x, y) => x + y } 14 | 15 | var mergedDictionary = HashMapMonoid(Sum).concat(dictionary1, dictionary2); 16 | 17 | console.log(mergedDictionary);//{and: 12, or: 4, the: 3} 18 | 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /monoids/monoid-homomorphisms/demo.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | export function demo() { 4 | 5 | var Sum = { empty: 0, concat: (x, y) => x + y } 6 | 7 | var bigList = [...Array(10e4)].map((_, i) => i); 8 | 9 | var slice = array => size => { 10 | var sliced = []; 11 | for (var i = 0; i < array.length; i += size) 12 | sliced.push(array.slice(i, i + size)) 13 | return sliced; 14 | } 15 | 16 | var mapReduce = array => f => m => 17 | array.map(chunk => chunk.map(f).reduce(m.concat, m.empty)) 18 | .reduce(m.concat, m.empty) 19 | 20 | var total = mapReduce(slice(bigList)(100))(x => x + 1)(Sum); 21 | console.log(total); 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /monoids/parenthesis.balancing/balance.js: -------------------------------------------------------------------------------- 1 | export class Balance { 2 | static Left = new Balance(0, 1); 3 | static Right = new Balance(1, 0); 4 | static Empty = new Balance(0, 0); 5 | 6 | constructor(l, r) { 7 | this.L = l; 8 | this.R = r; 9 | } 10 | isBalanced() { 11 | return this.L == Balance.Empty.L && this.R == Balance.Empty.R; 12 | } 13 | } 14 | 15 | export let toString = balance => 16 | `left:${balance.L},Right:${balance.R} is balanced:${balance.isBalanced()}`; 17 | 18 | export class BalanceMonoid /*implements monoid*/ { 19 | empty = Balance.Empty; 20 | concat(x, y) { 21 | if (x.R < y.L) return new Balance(x.L + y.L - x.R, y.R); 22 | else return new Balance(x.L, y.R + x.R - y.L); 23 | } 24 | } -------------------------------------------------------------------------------- /monoids/parenthesis.balancing/demo.js: -------------------------------------------------------------------------------- 1 | 2 | import { BalanceMonoid, toString } from "./balance.js"; 3 | import { parse } from "./parser.js"; 4 | 5 | export function demo() { 6 | let balanceMonoid = new BalanceMonoid(); 7 | 8 | let getBalance = input => 9 | Array.from(input) 10 | .map(parse) 11 | .reduce(balanceMonoid.concat, balanceMonoid.empty); 12 | 13 | console.log(toString(getBalance("((()))()"))); //{L:0,R:0} 14 | 15 | console.log(toString(getBalance("((()))("))); //{L:0,R:1} 16 | 17 | console.log(toString(getBalance(")((()))("))); //{L:1,R:1} 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /monoids/parenthesis.balancing/parser.js: -------------------------------------------------------------------------------- 1 | import { Balance } from "./balance.js"; 2 | 3 | export let parse = (c) => { 4 | switch (c) { 5 | case "(": 6 | return Balance.Left; 7 | case ")": 8 | return Balance.Right; 9 | default: 10 | return Balance.Empty; 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /monoids/predicate-monoidal-composition/Product.js: -------------------------------------------------------------------------------- 1 | export class Product { 2 | constructor(price, name) { 3 | this._price = price; 4 | this._name = name; 5 | } 6 | get name() { 7 | //getter and setters : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get 8 | return this._name; 9 | } 10 | 11 | get price() { 12 | return this._price; 13 | } 14 | static get name() { 15 | return c => c._name; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /monoids/predicate-monoidal-composition/demo.js: -------------------------------------------------------------------------------- 1 | 2 | import { Product } from "./Product.js"; 3 | 4 | export function demo() { 5 | 6 | var nameContains = pattern => product => product.name.includes(pattern); 7 | 8 | var highPriced = product => product.price > 1000; 9 | 10 | //operations 11 | var and = (predicate1, predicate2) => item => 12 | predicate1(item) && predicate2(item); 13 | 14 | var or = (predicate1, predicate2) => item => 15 | predicate1(item) || predicate2(item); 16 | 17 | var not = predicate1 => item => !predicate1(item); 18 | 19 | var products = [ 20 | new Product(100, "widgetA"), 21 | new Product(1100, "widgetB"), 22 | new Product(2000, "widgetA"), 23 | new Product(300, "widgetB") 24 | ]; 25 | 26 | { 27 | // For example, if we have a list of products we can filter them with their conjunction using and 28 | var productsAAndHighPriced = products 29 | .filter(and(highPriced, nameContains("A"))) 30 | .map(Product.name); 31 | 32 | console.log("productsA And High Priced"); 33 | console.log(productsAAndHighPriced); 34 | } 35 | 36 | { 37 | var productsBOrHighPriced = products 38 | .filter(or(highPriced, nameContains("B"))) 39 | .map(Product.name); 40 | 41 | console.log("products B Or High Priced"); 42 | console.log(productsBOrHighPriced); 43 | } 44 | 45 | { 46 | var productsNotBOrHighPriced = products 47 | .filter(not(or(highPriced, nameContains("B")))) 48 | .map(Product.name); 49 | 50 | console.log("products Not B Or HighPriced"); 51 | console.log(productsNotBOrHighPriced); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /monoids/products-of-monoids/demo.js: -------------------------------------------------------------------------------- 1 | 2 | export function demo() { 3 | 4 | var Sum = { empty: () => 0, concat: (x, y) => x + y } 5 | //var Mult = { empty: () => 1, concat: (x, y) => x * y } 6 | 7 | var Pair = (m1, m2) => ({ 8 | empty: () => ({ first: m1.empty(), second: m2.empty() }), 9 | concat: (x, y) => ({ 10 | first: m1.concat(x.first, y.first), 11 | second: m2.concat(x.second, y.second) 12 | }) 13 | }); 14 | 15 | var pair = Pair(Sum, Sum); 16 | 17 | var fold = [1, 2, 3, 4] 18 | .map(x => ({ first: x, second: 1 })) 19 | .reduce(pair.concat, pair.empty()); //{first:10, second:4} 20 | 21 | var average = fold.first / fold.second; 22 | console.log(average); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /monoids/specification-pattern/Product.js: -------------------------------------------------------------------------------- 1 | export class Product { 2 | constructor(price, name) { 3 | this._price = price; 4 | this._name = name; 5 | } 6 | get name() { 7 | //getter and setters : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get 8 | return this._name; 9 | } 10 | 11 | get price() { 12 | return this._price; 13 | } 14 | static get name() { 15 | return c => c._name; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /monoids/specification-pattern/Specification.js: -------------------------------------------------------------------------------- 1 | export class Specification { 2 | isSatisfiedBy(product) { 3 | throw "abstract Specification class "; 4 | } 5 | } 6 | 7 | //in order to add fluent builder semantics onto the And, Or,Not 8 | class CompositeSpecification extends Specification { 9 | and(spec) { 10 | return new And(this, new Spec(spec)); 11 | } 12 | or(spec) { 13 | return new Or(this, new Spec(spec)); 14 | } 15 | not() { 16 | return new Not(this); 17 | } 18 | } 19 | 20 | export class Spec extends CompositeSpecification { 21 | constructor(expression) { 22 | super(); 23 | this.expression = expression; 24 | } 25 | isSatisfiedBy(product) { 26 | return this.expression(product); 27 | } 28 | } 29 | 30 | class And extends CompositeSpecification { 31 | constructor(left, right) { 32 | super(); 33 | this.left = left; 34 | this.right = right; 35 | } 36 | isSatisfiedBy(product) { 37 | return this.left.isSatisfiedBy(product) && this.right.isSatisfiedBy(product); 38 | } 39 | } 40 | class Or extends CompositeSpecification { 41 | constructor(left, right) { 42 | super(); 43 | this.left = left; 44 | this.right = right; 45 | } 46 | isSatisfiedBy(product) { 47 | return this.left.isSatisfiedBy(product) || this.right.isSatisfiedBy(product); 48 | } 49 | } 50 | class Not extends CompositeSpecification { 51 | constructor(spec) { 52 | super(); 53 | this.spec = spec; 54 | } 55 | isSatisfiedBy(product) { 56 | return !this.spec.isSatisfiedBy(product); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /monoids/specification-pattern/demo.js: -------------------------------------------------------------------------------- 1 | import { Product } from "./Product.js"; 2 | import { Spec } from "./Specification.js"; 3 | 4 | export function demo() { 5 | 6 | var products = [ 7 | new Product(100, "widgetA"), 8 | new Product(1100, "widgetB"), 9 | new Product(2000, "widgetA"), 10 | new Product(300, "widgetB") 11 | ]; 12 | 13 | var nameContains = pattern => product => product.name.includes(pattern); 14 | 15 | var highPriced = product => product.price > 1000; 16 | 17 | { 18 | // For example, if we have a list of products we can filter them with their conjunction using and 19 | var spec1 = new Spec(nameContains(`A`)).and(highPriced); 20 | 21 | var products1 = products 22 | .filter(x => spec1.isSatisfiedBy(x)) 23 | .map(Product.name); 24 | 25 | console.log("productsA And High Priced"); 26 | console.log(products1); 27 | } 28 | 29 | { 30 | var spec2 = new Spec(nameContains(`A`)).or(highPriced); 31 | 32 | var products2 = products 33 | .filter(x => spec2.isSatisfiedBy(x)) 34 | .map(Product.name); 35 | 36 | console.log("products A Or High Priced"); 37 | console.log(products2); 38 | } 39 | 40 | { 41 | var spec3 = new Spec(nameContains(`A`)).or(highPriced).not(); 42 | 43 | var products3 = products 44 | .filter(x => spec3.isSatisfiedBy(x)) 45 | .map(Product.name); 46 | 47 | console.log("Not (products A Or High Priced)"); 48 | console.log(products3); 49 | } 50 | 51 | { 52 | var spec4 = new Spec(nameContains(`A`)).not().or(highPriced); 53 | 54 | var products4 = products 55 | .filter(x => spec4.isSatisfiedBy(x)) 56 | .map(Product.name); 57 | 58 | console.log("Not products A Or High Priced"); 59 | console.log(products4); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-console-app1", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "esm": { 8 | "version": "3.2.25", 9 | "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", 10 | "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" 11 | }, 12 | "fluture": { 13 | "version": "13.0.1", 14 | "resolved": "https://registry.npmjs.org/fluture/-/fluture-13.0.1.tgz", 15 | "integrity": "sha512-AhYpO5ZCsy781dtCW3XQH1XOasshr7+zn8aREWZq5FobkqVYp7EXBQnRpY/Rwf6Z4A+BLlOmW8T9U9yacDPppw==", 16 | "requires": { 17 | "sanctuary-show": "^2.0.0", 18 | "sanctuary-type-identifiers": "^3.0.0" 19 | } 20 | }, 21 | "folktale": { 22 | "version": "2.3.2", 23 | "resolved": "https://registry.npmjs.org/folktale/-/folktale-2.3.2.tgz", 24 | "integrity": "sha512-+8GbtQBwEqutP0v3uajDDoN64K2ehmHd0cjlghhxh0WpcfPzAIjPA03e1VvHlxL02FVGR0A6lwXsNQKn3H1RNQ==" 25 | }, 26 | "install": { 27 | "version": "0.13.0", 28 | "resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz", 29 | "integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==" 30 | }, 31 | "node": { 32 | "version": "15.0.0", 33 | "resolved": "https://registry.npmjs.org/node/-/node-15.0.0.tgz", 34 | "integrity": "sha512-eianPjwhzeGqYIdkq8JmMA6X7IIpbba4twNx5jQ/tvqKNc7ffCunXH2W3ItKTzpiziUnGZ8bcH/ckCr7GNJ1fA==", 35 | "requires": { 36 | "node-bin-setup": "^1.0.0" 37 | } 38 | }, 39 | "node-bin-setup": { 40 | "version": "1.0.6", 41 | "resolved": "https://registry.npmjs.org/node-bin-setup/-/node-bin-setup-1.0.6.tgz", 42 | "integrity": "sha512-uPIxXNis1CRbv1DwqAxkgBk5NFV3s7cMN/Gf556jSw6jBvV7ca4F9lRL/8cALcZecRibeqU+5dFYqFFmzv5a0Q==" 43 | }, 44 | "node-fetch": { 45 | "version": "2.6.1", 46 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", 47 | "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" 48 | }, 49 | "pratica": { 50 | "version": "2.0.3", 51 | "resolved": "https://registry.npmjs.org/pratica/-/pratica-2.0.3.tgz", 52 | "integrity": "sha512-MXOO7NtsxjZBInCPK7iFLEgthZ0EH9QJEBmE6xF6+0rA71IS2HLITV8LJ1ByrTfzRVSqfvZMOmFqkEhkZ/nqSw==" 53 | }, 54 | "ramda": { 55 | "version": "0.27.1", 56 | "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", 57 | "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==" 58 | }, 59 | "rxjs": { 60 | "version": "6.6.3", 61 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", 62 | "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", 63 | "requires": { 64 | "tslib": "^1.9.0" 65 | } 66 | }, 67 | "rxjs-compat": { 68 | "version": "6.6.3", 69 | "resolved": "https://registry.npmjs.org/rxjs-compat/-/rxjs-compat-6.6.3.tgz", 70 | "integrity": "sha512-y+wUqq7bS2dG+7rH2fNMoxsDiJ32RQzFxZQE/JdtpnmEZmwLQrb1tCiItyHxdXJHXjmHnnzFscn3b6PEmORGKw==" 71 | }, 72 | "sanctuary": { 73 | "version": "3.1.0", 74 | "resolved": "https://registry.npmjs.org/sanctuary/-/sanctuary-3.1.0.tgz", 75 | "integrity": "sha512-yTpWmslb5Q2jXcHYeIfCzJksIpe3pngBaFBDpjdPXONWyVSB3hBVDZZ8EmitohaMZqjLB+JREeEL0YOVtdxILA==", 76 | "requires": { 77 | "sanctuary-def": "0.22.0", 78 | "sanctuary-either": "2.1.0", 79 | "sanctuary-maybe": "2.1.0", 80 | "sanctuary-pair": "2.1.0", 81 | "sanctuary-show": "2.0.0", 82 | "sanctuary-type-classes": "12.1.0", 83 | "sanctuary-type-identifiers": "3.0.0" 84 | } 85 | }, 86 | "sanctuary-def": { 87 | "version": "0.22.0", 88 | "resolved": "https://registry.npmjs.org/sanctuary-def/-/sanctuary-def-0.22.0.tgz", 89 | "integrity": "sha512-lywS27TfuPHzelMh6M1YWBcB//Lom4Gko/tGjKYbDPkPet1B5KplsSdjY+5sF4mwp6+PDUodsk/Y3Uskrc/ebw==", 90 | "requires": { 91 | "sanctuary-either": "2.1.0", 92 | "sanctuary-show": "2.0.0", 93 | "sanctuary-type-classes": "12.1.0", 94 | "sanctuary-type-identifiers": "3.0.0" 95 | } 96 | }, 97 | "sanctuary-either": { 98 | "version": "2.1.0", 99 | "resolved": "https://registry.npmjs.org/sanctuary-either/-/sanctuary-either-2.1.0.tgz", 100 | "integrity": "sha512-AsQCma2yGAVS7Dlxme/09NZWQfcXBNylVmkuvarp8uOe9otSwClOgQyyePN2OxO4hTf3Au1Ck4ggm+97Je06kA==", 101 | "requires": { 102 | "sanctuary-show": "2.0.0", 103 | "sanctuary-type-classes": "12.1.0" 104 | } 105 | }, 106 | "sanctuary-maybe": { 107 | "version": "2.1.0", 108 | "resolved": "https://registry.npmjs.org/sanctuary-maybe/-/sanctuary-maybe-2.1.0.tgz", 109 | "integrity": "sha512-xCmvaEkYaIz5klWUrsgvgORKTL3bVYxATp7HyGBY0ZCu9tHJtSYDTpwnqMkRknHFIU986Kr2M/dJdiFvuc6UCQ==", 110 | "requires": { 111 | "sanctuary-show": "2.0.0", 112 | "sanctuary-type-classes": "12.1.0" 113 | } 114 | }, 115 | "sanctuary-pair": { 116 | "version": "2.1.0", 117 | "resolved": "https://registry.npmjs.org/sanctuary-pair/-/sanctuary-pair-2.1.0.tgz", 118 | "integrity": "sha512-yY1JzzIJ/Ex7rjN8NmQLB7yv6GVzNeaMvxoYSW8w1ReLQI0n2sGOVCnAVipZuCv7wmfd+BavSXp59rklJeYytw==", 119 | "requires": { 120 | "sanctuary-show": "2.0.0", 121 | "sanctuary-type-classes": "12.1.0" 122 | } 123 | }, 124 | "sanctuary-show": { 125 | "version": "2.0.0", 126 | "resolved": "https://registry.npmjs.org/sanctuary-show/-/sanctuary-show-2.0.0.tgz", 127 | "integrity": "sha512-REj4ZiioUXnDLj6EpJ9HcYDIEGaEexmB9Fg5o6InZR9f0x5PfnnC21QeU9SZ9E7G8zXSZPNjy8VRUK4safbesw==" 128 | }, 129 | "sanctuary-type-classes": { 130 | "version": "12.1.0", 131 | "resolved": "https://registry.npmjs.org/sanctuary-type-classes/-/sanctuary-type-classes-12.1.0.tgz", 132 | "integrity": "sha512-oWP071Q88dEgJwxHLZp8tc7DoS+mWmYhYOuhP85zznPpIxV1ZjJfyRvcR59YCazyFxyFeBif7bJx1giLhDZW0Q==", 133 | "requires": { 134 | "sanctuary-type-identifiers": "3.0.0" 135 | } 136 | }, 137 | "sanctuary-type-identifiers": { 138 | "version": "3.0.0", 139 | "resolved": "https://registry.npmjs.org/sanctuary-type-identifiers/-/sanctuary-type-identifiers-3.0.0.tgz", 140 | "integrity": "sha512-YFXYcG0Ura1dSPd/1xLYtE2XAWUEsBHhMTZvYBOvwT8MeFQwdUOCMm2DC+r94z6H93FVq0qxDac8/D7QpJj6Mg==" 141 | }, 142 | "tslib": { 143 | "version": "1.14.1", 144 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 145 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-console-app1", 3 | "version": "0.0.0", 4 | "description": "code repo for the book: functional Js with categories", 5 | "main": "app.mjs", 6 | "author": { 7 | "name": "dimitris papadimitriou" 8 | }, 9 | "scripts": { 10 | "test": "mocha 'test/**'" 11 | }, 12 | "type": "module", 13 | "dependencies": { 14 | "crocks": "^0.12.4", 15 | "esm": "^3.2.25", 16 | "fluture": "^13.0.1", 17 | "folktale": "^2.3.2", 18 | "install": "^0.13.0", 19 | "mocha": "^8.2.0", 20 | "node": "^15.0.0", 21 | "node-fetch": ">=2.6.1", 22 | "pratica": "^2.0.3", 23 | "ramda": "^0.27.1", 24 | "rxjs": "^6.6.3", 25 | "rxjs-compat": "^6.6.3", 26 | "sanctuary": "^3.1.0" 27 | }, 28 | "devDependencies": {} 29 | } 30 | -------------------------------------------------------------------------------- /test/functors/maybe.js: -------------------------------------------------------------------------------- 1 | import { Some, None } from "../../functors/Maybe.js" 2 | import assert from "assert" 3 | 4 | describe('Maybe', function () { 5 | describe('Some.map()', function () { 6 | it('should map corectly simple types', function () { 7 | 8 | var result = Some(2) 9 | .map(x => x * x) 10 | .match({ 11 | some: v => v, 12 | none: () => -1 13 | }); 14 | 15 | assert.deepStrictEqual(result, 4) 16 | }); 17 | }); 18 | describe('None.map()', function () { 19 | it('should map corectly simple types', function () { 20 | 21 | var result = None() 22 | .map(x => x * x) 23 | .match({ 24 | some: v => v, 25 | none: () => -1 26 | }); 27 | 28 | assert.deepStrictEqual(result, -1) 29 | }); 30 | }); 31 | }); -------------------------------------------------------------------------------- /test/transformations/IOToReader.js: -------------------------------------------------------------------------------- 1 | import assert from "assert" 2 | import { IOToReader } from "../../common/IOToReader.js" 3 | import { IO } from "../../monads/IO.js" 4 | import { Reader } from "../../monads/Reader.js" 5 | 6 | 7 | describe('IOToReader', function () { 8 | it('should return IO value on bind', function () { 9 | 10 | var actual = IOToReader(IO(() => `io`)) 11 | .bind(value => Reader(env => env.x + value + env.y)) 12 | .run({ x: `x`, y: `y` }) 13 | 14 | assert.strict.equal(actual, `xioy`) 15 | }); 16 | 17 | it('should return IO value on bind', function () { 18 | 19 | var actual = IOToReader(IO(() => `io`)) 20 | .bind(value => Reader(env => env.x + value + env.y)) 21 | .bind(value => Reader(env => env.x + value + env.y)) 22 | .run({ x: `x`, y: `y` }) 23 | 24 | assert.strict.equal(actual, `xxioyy`) 25 | }); 26 | 27 | }); 28 | 29 | -------------------------------------------------------------------------------- /test/transformations/IOToState.js: -------------------------------------------------------------------------------- 1 | import assert from "assert" 2 | import { IOToState } from "../../common/IOToState.js" 3 | import { IO } from "../../monads/IO.js" 4 | import { State } from "../../monads/State.js" 5 | 6 | 7 | describe('IOToState', function () { 8 | 9 | it('should return IO value on bind', function () { 10 | 11 | var initialState = ({ x: `x`, y: `y` }); 12 | 13 | var actual = IOToState(IO(() => `io`)) 14 | .bind(value => State(s => ({ value: s.x + value + s.y, state: s }))) 15 | .run(initialState); 16 | 17 | assert.strict.equal(actual.value, `xioy`); 18 | assert.strict.deepEqual(actual.state, initialState); 19 | }); 20 | 21 | it('should return IO value on bind', function () { 22 | 23 | var initialState = ({ x: `x`, y: `y` }); 24 | 25 | var actual = IOToState(IO(() => `io`)) 26 | .bind(value => State(s => ({ value: s.x + value + s.y, state: s }))) 27 | .bind(value => State(s => ({ value: s.x + value + s.y, state: s }))) 28 | .run(initialState); 29 | 30 | assert.strict.equal(actual.value, `xxioyy`); 31 | assert.strict.deepEqual(actual.state, initialState); 32 | }); 33 | 34 | 35 | }); 36 | 37 | -------------------------------------------------------------------------------- /test/transformations/eitherToPromise.js: -------------------------------------------------------------------------------- 1 | import assert from "assert" 2 | import { eitherToPromise } from "../../common/eitherToPromise.js" 3 | import { Left, Right } from "../../monads/Either.js" 4 | 5 | describe('eitherToPromise', function () { 6 | it('should resolve when Right', function () { 7 | return eitherToPromise(Right(5)).then(v => { 8 | assert.strict.equal(v, 5) 9 | }).catch(error => { 10 | assert.fail("should not reach reject") 11 | }) 12 | }); 13 | 14 | it('should reject when Left with the value as reject reason', function (done) { 15 | eitherToPromise(Left(-1)).then(v => { 16 | assert.fail("should not reach resolve"); 17 | done() 18 | }).catch(error => { 19 | assert.strict.equal(error, -1) 20 | done(); 21 | }) 22 | }); 23 | }); 24 | 25 | -------------------------------------------------------------------------------- /test/transformations/firstOrNone.js: -------------------------------------------------------------------------------- 1 | import assert from "assert" 2 | import { firstOrNone } from "../../common/firstOrNone.js" 3 | 4 | describe('firstOrNone', function () { 5 | it('should match none when empty array', function () { 6 | var actual = firstOrNone([]).match({ 7 | some: v => v, 8 | none: () => -1 9 | }) 10 | assert.strict.equal(actual, -1) 11 | }); 12 | 13 | it('should match Some with the first value when array is non empty ', function () { 14 | var actual = firstOrNone([1,2,3,4]).match({ 15 | some: v => v, 16 | none: () => -1 17 | }) 18 | assert.strict.equal(actual, 1) 19 | }); 20 | 21 | // describe("firstOrNone commuting diagram", function() { 22 | // it("should preserve structure when empty array", function() { 23 | // var path1 = firstOrNone([]).map(x => x + 1); 24 | 25 | // var path2 = firstOrNone([].map(x => x + 1)); 26 | 27 | // expect(match(-1)(path1)).to.deep.equal(match(-1)(path2)); 28 | // }); 29 | 30 | // it("should preserve structure when non-empty array", function() { 31 | // var array = [1, 2, 3, 4, 5]; 32 | // var path1 = firstOrNone(array).map(x => x + 1); 33 | 34 | // var path2 = firstOrNone(array.map(x => x + 1)); 35 | 36 | // expect(match(-1)(path1)).to.deep.equal(match(-1)(path2)); 37 | // }); 38 | // }); 39 | }); 40 | 41 | -------------------------------------------------------------------------------- /test/transformations/maybeToEither.js: -------------------------------------------------------------------------------- 1 | import assert from "assert" 2 | import { maybeToEither } from "../../common/maybeToEither.js" 3 | import { Some, None } from "../../monads/Maybe.js" 4 | 5 | describe('maybeToEither', function () { 6 | it('should match right when Some', function () { 7 | var actual = maybeToEither(-1)(Some(5)).match({ 8 | right: v => v, 9 | left: e => e 10 | }) 11 | assert.strict.equal(actual, 5) 12 | }); 13 | 14 | it('should match left when None', function () { 15 | var actual = maybeToEither(-1)(None()) 16 | .match({ 17 | right: v => v, 18 | left: e => e 19 | }) 20 | assert.strict.equal(actual, -1) 21 | }); 22 | }); 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/transformations/maybeToPromise.js: -------------------------------------------------------------------------------- 1 | import assert from "assert" 2 | import { maybeToPromise } from "../../common/maybeToPromise.js" 3 | import { Some, None } from "../../monads/Maybe.js" 4 | 5 | describe('maybeToPromise', function () { 6 | it('should resolve when Some', function () { 7 | return maybeToPromise(-1)(Some(5)).then(v => { 8 | assert.strict.equal(v, 5) 9 | }).catch(error => { 10 | assert.fail("should not reach reject") 11 | }) 12 | }); 13 | 14 | it('should reject when None', function (done) { 15 | maybeToPromise(-1)(None()).then(v => { 16 | assert.fail("should not reach resolve"); 17 | done() 18 | }).catch(error => { 19 | assert.strict.equal(error, -1) 20 | done(); 21 | }) 22 | }); 23 | }); 24 | 25 | -------------------------------------------------------------------------------- /test/transformations/optionalToMaybe.js: -------------------------------------------------------------------------------- 1 | import assert from "assert" 2 | import { optionalToMaybe } from "../../common/optionalToMaybe.js" 3 | 4 | describe('optionalToMaybe', function () { 5 | it('should match none when null', function () { 6 | var actual = optionalToMaybe(null).match({ 7 | some: v => v, 8 | none: () => -1 9 | }) 10 | assert.strict.equal(actual, -1) 11 | }); 12 | 13 | it('should match Some when not null', function () { 14 | var actual = optionalToMaybe(5).match({ 15 | some: v => v, 16 | none: () => -1 17 | }) 18 | assert.strict.equal(actual, 5) 19 | }); 20 | }); 21 | 22 | -------------------------------------------------------------------------------- /test/transformations/stateToReader.js: -------------------------------------------------------------------------------- 1 | import assert from "assert" 2 | import { stateToReader } from "../../common/stateToReader.js" 3 | import { Reader } from "../../monads/Reader.js" 4 | import { State } from "../../monads/State.js" 5 | 6 | describe('stateToReader', function () { 7 | it('should map correctly', function () { 8 | 9 | var env = ({ x: `x`, y: `y` }); 10 | 11 | var actual = stateToReader(State(s => ({ value: env.x + `state` + env.y, state: s }))) 12 | .map(value => `map(${value})`) 13 | .run(env) 14 | 15 | assert.strict.equal(actual, 'map(xstatey)') 16 | }); 17 | 18 | it('should bind correctly', function () { 19 | 20 | var env = ({ x: `x`, y: `y` }); 21 | 22 | var actual = stateToReader(State(s => ({ value: env.x + `state` + env.y, state: s }))) 23 | .bind(value => Reader(env => `reader` + env.x + value + env.y)) 24 | .run(env) 25 | 26 | assert.strict.equal(actual, 'readerxxstateyy') 27 | }); 28 | 29 | 30 | }); 31 | 32 | -------------------------------------------------------------------------------- /transformations/maybeToPromise/demo.js: -------------------------------------------------------------------------------- 1 | import { maybeToPromise } from "../../common/maybeToPromise.js" 2 | import { Some, None } from "../../monads/Maybe.js" 3 | 4 | export function demo() { 5 | 6 | maybeToPromise(-1)(Some(`5`)).then(v => console.log(`success${v}`)).catch(error => console.log(`success${error}`)) 7 | 8 | 9 | } 10 | --------------------------------------------------------------------------------