Skip to content

Instantly share code, notes, and snippets.

@trygvea
Last active June 29, 2016 08:04
Show Gist options
  • Save trygvea/0d5bc725242f6e956821be8ab8cc0078 to your computer and use it in GitHub Desktop.
Save trygvea/0d5bc725242f6e956821be8ab8cc0078 to your computer and use it in GitHub Desktop.
Pure stateful computations in Javascript (ES6)
// push & pop as pure stateful computations
const push = elem => stack => [null, [elem, ...stack]]
const pop = ([head, ...tail]) => [head, tail]
// ----------------------------------------------------------
// Let's do a simple stack manipulation popping the first two
// elements and pushing their product back on to the stack.
// The boring way to do it: Note how we manually must lift
// the stack through computations.
// (compare that to stackManipM below)
const stackManip = stack => {
const [first, stack1] = pop(stack)
const [second, stack2] = pop(stack1)
return push(first*second)(stack2)
}
console.log("stackManip:");console.log(stackManip([3,2,1]))
// ----------------------------------------------------------
// The monadic way. First define two helper functions
// Decorate a pure stateful computation with a flatMap
const statefulComputation = u => {
const h = (...args) => u(...args)
h.flatMap = f => statefulComputation ( s => {
const [a, newState] = h(s)
const g = f(a)
return g(newState)
})
return h
}
// A haskell simulation of do (or scala for comprehension).
// Here, we traverse the computations (or any other monad)
// step by step from the js function * generator, combining
// steps with flatMap.
// Taken from https://curiosity-driven.org/monads-in-javascript
const doM = steps => {
const nextStep = value => {
const { done, value: nextValue } = steps.next(value)
return done ? nextValue : nextValue.flatMap(nextStep)
}
return nextStep()
}
// Define monadic versions of push & pop
const pushM = elem => statefulComputation(push(elem))
const popM = statefulComputation(pop)
// ----------------------------------------------------------
// And now, the monadic variant of stackManip:
// Note no references to stack in implementation!
// (compare to stackManip above)
const stackManipM = stack => doM(function*() {
const first = yield popM
const second = yield popM
return pushM(first * second)
}())(stack)
console.log("stackManipM:");console.log(stackManipM([3,2,1]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment