Last active
July 24, 2018 16:15
-
-
Save kana-sama/29dc32b190c66828e3ebdf323cbfa36d to your computer and use it in GitHub Desktop.
monadic selectors
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
console.clear(); | |
function compose(...fs) { | |
return function (...args) { | |
return fs.reduceRight((args, f) => [f(...args)], args)[0]; | |
}; | |
} | |
function simpleCheck(a, b) { | |
return a === b; | |
} | |
function simpleCheckArray(a, b) { | |
if (a.length === 0 && b.length === 0) return true; | |
if (a.length === 0 || b.length === 0) return false; | |
const [x, ...xs] = a; | |
const [y, ...ys] = b; | |
return simpleCheck(x, y) && simpleCheckArray(xs, ys); | |
} | |
function memoize(fn) { | |
let prevArgs = null; | |
let prevResult = null; | |
return function cachedFn(...args) { | |
if (!prevArgs || !simpleCheckArray(args, prevArgs)) { | |
prevArgs = args; | |
prevResult = fn(...args); | |
} | |
return prevResult; | |
}; | |
} | |
function Do(generatorFunction) { | |
const generator = generatorFunction(); | |
function next(error, value) { | |
const res = generator.next(value); | |
if (res.done) { | |
return res.value; | |
} else { | |
return res.value.chain(value => next(null, value) || res.value.of(value)); | |
} | |
}; | |
return next(); | |
} | |
class Selector { | |
constructor(unwrappedSelector) { | |
this.unwrappedSelector = memoize(unwrappedSelector); | |
this.apply = memoize((f, x) => f(x)); | |
} | |
static of(value) { | |
return new Selector((state, props) => value); | |
} | |
of(value) { | |
return Selector.of(value); | |
} | |
run(state, props) { | |
return this.unwrappedSelector(state, props); | |
} | |
map(fn) { | |
return new Selector(compose(memoize(fn), this.unwrappedSelector)); | |
} | |
ap(fn) { | |
return new Selector((state, props) => { | |
const x = this.run(state, props); | |
const f = fn.run(state, props); | |
return this.apply(f, x); | |
}); | |
} | |
join() { | |
return new Selector((state, props) => { | |
return this.run(state, props).run(state, props); | |
}); | |
} | |
chain(fn) { | |
return this.map(fn).join(); | |
} | |
} | |
const usersEntities = new Selector((state, props) => state.users.entities); | |
const usersIds = new Selector((state, props) => state.users.ids); | |
const users_monad = usersEntities.chain(entities => { | |
return usersIds.chain(ids => { | |
console.log("recalculate monad"); | |
return Selector.of(ids.map(id => entities[id])); | |
}); | |
}); | |
const users_applicative = usersEntities.ap(usersIds.map( | |
ids => entities => { | |
console.log("recalculate applicative"); | |
return ids.map(id => entities[id]); | |
} | |
)); | |
const users_do = Do(function* () { | |
const ids = yield usersIds; | |
const entities = yield usersEntities; | |
console.log("recalculate do"); | |
return Selector.of(ids.map(id => entities[id])); | |
}); | |
const state = { | |
users: { | |
ids: [1, 2, 4], | |
entities: { | |
1: { id: 1, name: "kana" }, | |
2: { id: 2, name: "aleph" }, | |
4: { id: 4, name: "andrew" } | |
} | |
} | |
}; | |
const props = {}; | |
console.log("1", users_monad.run(state, props)); | |
console.log("2", users_applicative.run(state, props)); | |
console.log("3", users_do.run(state, props)); | |
console.log(" "); | |
console.log("4", users_monad.run(state, props)); | |
const newState = { ...state }; | |
// const newState = { users: { ...state.users, ids: [1, 2] } }; | |
console.log("5", users_monad.run(newState, props)); | |
console.log("6", users_monad.run(newState, props)); | |
console.log("7", users_monad.run(state, props)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment