$ npm install
$ Run
$ babel-node <...>.js
/node_modules |
const SuccessEvent = () => ({type: 'SuccessEvent'}) | |
const FailureEvent = () => ({type: 'FailureEvent'}) | |
const operationOnEntity3 = entity1 => entity2 => entity3 => { | |
if (entity1.state.isGood && entity2.state.isGreat) { | |
return entity3.append(SuccessEvent()) | |
} else { | |
return entity3.append(FailureEvent()) | |
} | |
} | |
const Command = fnP => { | |
const ap = argP => Command(fnP.then(f => argP.then(arg => f(arg)))) | |
const then = (f, e) => fnP.then(f, e) | |
return {ap, then} | |
} | |
const op = Promise.resolve(operationOnEntity3) | |
const e1 = Promise.resolve({state:{isGood: true}}) | |
const e2 = Promise.resolve({state:{isGreat: true}}) | |
const e3 = Promise.resolve({ | |
append: e => Promise.resolve(e) | |
}) | |
//op.then(opFn => e1.then(e => opFn(e))) | |
// .then(opFn => e2.then(e => opFn(e))) | |
// .then(opFn => e3.then(e => opFn(e))) | |
// .then(console.log, console.error) | |
Command(op) | |
.ap(e1) | |
.ap(e2) | |
.ap(e3) | |
.then(console.log, console.error) |
import daggy from 'daggy' | |
const UnitEvent = daggy.taggedSum({ | |
UnitRegistered: ['serialNumber'], | |
UnitProvisioned: ['profile'], | |
UnitDecommissioned: ['time'] | |
}) | |
console.log(UnitEvent.UnitRegistered('123')) | |
console.log(typeof UnitEvent.UnitRegistered('123')) | |
console.log(UnitEvent.UnitRegistered('123') instanceof UnitEvent) | |
console.log(UnitEvent.UnitRegistered('123') instanceof UnitEvent.UnitRegistered) | |
console.log(UnitEvent.UnitRegistered('123') instanceof UnitEvent.UnitProvisioned) | |
UnitEvent.UnitRegistered('123').cata({ | |
UnitRegistered: (sn) => console.log(sn), | |
UnitProvisioned: (sn, p) => console.log(sn, p) | |
}) | |
const history = [ | |
UnitEvent.UnitRegistered('123'), | |
UnitEvent.UnitProvisioned({a: 1, b: 2}), | |
UnitEvent.UnitDecommissioned(new Date()) | |
] | |
const toEndofunction = event => event.cata({ | |
UnitRegistered: (serialNumber) => state => ({status: 'registered', serialNumber}), | |
UnitProvisioned: (profile) => state => ({...state, status: 'provisioned', profile}), | |
UnitDecommissioned: (time) => state => ({...state, status: 'decommissioned', time}) | |
}) | |
const compose = (fns) => fns.reduce((composed, fn) => s => fn(composed(s)), s => s) | |
console.log(compose(history.map(toEndofunction))({})) | |
class UnitBaseEvent { | |
constructor(serialNumber) { | |
this.serialNumber = serialNumber | |
} | |
match (spec) { | |
return spec[this.constructor.name](this) | |
} | |
} | |
class UnitProvisioned extends UnitBaseEvent { | |
constructor (sn, a) { | |
super(sn) | |
this.a = a | |
} | |
} | |
class UnitRegistered extends UnitBaseEvent { | |
static REGID = 123 | |
} | |
console.log(new UnitProvisioned('123', 1)) | |
console.log(new UnitProvisioned('123', 2) instanceof UnitProvisioned) | |
console.log(new UnitProvisioned('123', 2) instanceof UnitBaseEvent) | |
const event = new UnitProvisioned('123', 2) | |
event.match({ | |
UnitProvisioned: (e) => { console.log('UP ' + e.a) }, | |
UnitRegistered: (e) => { console.log('UR') } | |
}) | |
console.log([new UnitProvisioned('123', 1), new UnitProvisioned('123', 2), new UnitProvisioned('123', 3), new UnitRegistered('123')]) |
import {Free} from 'monet' | |
console.log(Free.Return(1)) |
const Nothing = () => ({ | |
bind: _ => Nothing(), | |
run: f => f(null) | |
}) | |
const Just = value => ({ | |
bind: f => f(value), | |
run: f => f(value) | |
}) | |
const half = number => number % 2 === 0 ? Just(number / 2) : Nothing() | |
Just(32).bind(half).bind(half).bind(half).run(console.log) | |
Just(41).bind(half).bind(half).bind(half).run(console.log) | |
{ | |
"name": "js-monads", | |
"version": "1.0.0", | |
"description": "", | |
"main": "maybeMonad.js", | |
"scripts": { | |
"test": "echo \"Error: no test specified\" && exit 1" | |
}, | |
"repository": { | |
"type": "git", | |
"url": "git+ssh://[email protected]/ee2e87db5c49f0613434.git" | |
}, | |
"author": "", | |
"license": "ISC", | |
"bugs": { | |
"url": "https://gist.github.com/ee2e87db5c49f0613434" | |
}, | |
"homepage": "https://gist.github.com/ee2e87db5c49f0613434", | |
"dependencies": { | |
"daggy": "0.0.1", | |
"monet": "^0.8.6" | |
}, | |
"devDependencies": { | |
"babel-preset-es2015-node4": "^2.0.2", | |
"babel-preset-stage-0": "^6.3.13" | |
}, | |
"babel": { | |
"presets": [ | |
"es2015-node4", | |
"stage-0" | |
] | |
} | |
} |
// http://adit.io/posts/2013-06-10-three-useful-monads.html | |
// f :: env -> result | |
const Reader = f => { | |
const run = env => f(env) | |
const bind = func => Reader(env => func(run(env)).run(env)) | |
return { run, bind } | |
} | |
// Example 1 | |
const sharedEnv = { | |
'first': 'First', | |
'second': 'Second', | |
'keys': ['first', 'second'] | |
} | |
const r1 = Reader(env => env['first']) | |
console.log(r1.run(sharedEnv)) // 'First value' | |
const r2 = Reader(env => Object.keys(env)) | |
console.log(r2.run(sharedEnv)) // [ 'first', 'second', 'keys' ] | |
console.log(Reader(env => env['keys']).bind(keys => Reader(env => keys.map(key => env[key]))).run(sharedEnv)) // [ 'First', 'Second' ] | |
// Example 2 | |
const Database = () => { | |
const artists = [ | |
{ id: 1, name: 'Ringo' }, | |
{ id: 2, name: 'John' }, | |
{ id: 3, name: 'George' }, | |
{ id: 4, name: 'Paul' } | |
] | |
const bands = [{ id: 1, name: 'The Beatles', members: [1, 2, 3, 4] }] | |
return { | |
findBandByName: name => bands.find(band => band.name === name), | |
getArtist: id => artists.find(artist => artist.id === id) | |
} | |
} | |
const db = Database() // this is the shared environment | |
const lookupBand = name => Reader(db => db.findBandByName(name)) | |
const artistsForBand = band => Reader(db => band.members.map(id => db.getArtist(id))) | |
const greatestBand = Reader(_ => 'The Beatles') | |
const greatestMusicians = greatestBand.bind(lookupBand).bind(artistsForBand) | |
console.log(greatestMusicians.run(db)) |
const combineReaders = (r1, r2) => v1 => env => { | |
const v2 = r1(v1)(env) | |
const v3 = r2(v2)(env) | |
return v3 | |
} | |
// Example 1 | |
const sharedEnv = { | |
'first': 'First', | |
'second': 'Second', | |
'keys': ['first', 'second'] | |
} | |
const r1 = env => env['first'] | |
console.log(r1(sharedEnv)) // 'First value' | |
const r2 = env => Object.keys(env) | |
console.log(r2(sharedEnv)) // [ 'first', 'second', 'keys' ] | |
console.log(combineReaders((name => env => env[name]), (keys => env => keys.map(key => env[key])))('keys')(sharedEnv)) // [ 'First', 'Second' ] | |
// Example 2 | |
const Database = () => { | |
const artists = [ | |
{ id: 1, name: 'Ringo' }, | |
{ id: 2, name: 'John' }, | |
{ id: 3, name: 'George' }, | |
{ id: 4, name: 'Paul' } | |
] | |
const bands = [{ id: 1, name: 'The Beatles', members: [1, 2, 3, 4] }] | |
return { | |
findBandByName: name => bands.find(band => band.name === name), | |
getArtist: id => artists.find(artist => artist.id === id) | |
} | |
} | |
const db = Database() // this is the shared environment | |
const lookupBand = name => db => db.findBandByName(name) | |
const artistsForBand = band => db => band.members.map(id => db.getArtist(id)) | |
const greatestMusicians = combineReaders(lookupBand, artistsForBand)('The Beatles') | |
console.log(greatestMusicians(db)) | |
const combineReaders2 = (r1, r2) => env => r2(r1(env))(env) | |
const greatestMusicians2 = combineReaders2(lookupBand('The Beatles'), band => artistsForBand(band)) | |
console.log(greatestMusicians2(db)) |
const Writer = (value, log = []) => { | |
const run = () => ({ value, log }) | |
const bind = f => { | |
const r = f(value).run() | |
return Writer(r.value, [...log, ...r.log]) | |
} | |
return { run, bind } | |
} | |
const incBy = number => x => Writer(x + number, [`incremented ${x} by ${number}`]) | |
const multiplyBy = number => x => Writer(x * number, [`multiplied ${x} by ${number}`]) | |
console.log(Writer(4).bind(multiplyBy(3)).bind(incBy(1)).run()) | |
const Writer = (empty, append) => (value, accumulator = empty) => { | |
const run = () => ({ value, accumulator }) | |
const bind = f => { | |
const r = f(value).run() | |
return Writer(empty, append)(r.value, append(accumulator, r.accumulator)) | |
} | |
return { run, bind } | |
} | |
const NumberWithTrace = Writer([], (a, b) => a.concat(b)) | |
const incBy = number => x => NumberWithTrace(x + number, [`incremented ${x} by ${number}`]) | |
const multiplyBy = number => x => NumberWithTrace(x * number, [`multiplied ${x} by ${number}`]) | |
console.log(NumberWithTrace(4).bind(multiplyBy(3)).bind(incBy(1)).run()) | |
// Example with IO side-effect | |
const XXX = Writer(_ => _, (a, b) => () => b(a())) | |
const op1 = number => x => XXX(x + number, () => console.log(`incremented ${x} by ${number}`)) | |
const op2 = number => x => XXX(x * number, () => console.log(`multiplied ${x} by ${number}`)) | |
const res = XXX(4).bind(op1(10)).bind(op2(2)).run() | |
console.log(res.value) | |
res.accumulator() |
const Writer = (empty, append) => (value, accumulator = empty) => { | |
const bind = f => { | |
const r = f(value) | |
return Writer(empty, append)(r.value, append(accumulator, r.accumulator)) | |
} | |
return { value, accumulator, bind } | |
} | |
const NumberWithTrace = Writer([], (a, b) => a.concat(b)) | |
const incBy = number => x => NumberWithTrace(x + number, [`incremented ${x} by ${number}`]) | |
const multiplyBy = number => x => NumberWithTrace(x * number, [`multiplied ${x} by ${number}`]) | |
console.log(NumberWithTrace(4).bind(multiplyBy(3)).bind(incBy(1))) | |
// Example with IO side-effect | |
const XXX = Writer(_ => _, (a, b) => () => b(a())) | |
const op1 = number => x => XXX(x + number, () => console.log(`incremented ${x} by ${number}`)) | |
const op2 = number => x => XXX(x * number, () => console.log(`multiplied ${x} by ${number}`)) | |
const res = XXX(4).bind(op1(10)).bind(op2(2)) | |
console.log(res.value) | |
res.accumulator() | |
// Example with Promise IO, accumulator: Promise | |
const log = message => new Promise((resolve, _) => { | |
console.log(message); | |
resolve() | |
}) | |
const Promised = Writer(Promise.resolve(), (a, b) => a.then(_ => b)) | |
const res1 = Promised(1).bind(n => Promised(n + 2, log(`incremented ${n} by 2`))) | |
console.log(res1.value) | |
res1.accumulator.then(_=>console.log('done')) | |
// Example with Promise IO, accumulator: () => Promise | |
const Promised2 = Writer(() => Promise.resolve(), (a, b) => () => a().then(_ => b())) | |
const res2 = Promised2(1).bind(n => Promised2(n + 2, () => log(`incremented ${n} by 2`))) | |
console.log(res2.value) | |
res2.accumulator().then(_=>console.log('done')) |