$ 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')) |