Skip to content

Instantly share code, notes, and snippets.

@trygvea
Last active October 20, 2016 21:24
Show Gist options
  • Save trygvea/fe07eb066ba8c23b3600368ee61dad2d to your computer and use it in GitHub Desktop.
Save trygvea/fe07eb066ba8c23b3600368ee61dad2d to your computer and use it in GitHub Desktop.
Pure stateful computations in Javascript (ES6), reloaded
// pureStatefulComputation: (state) => [result, newState]
// push & pop as pure stateful computations
const push = elem => stack => [undefined, [elem, ...stack]]
const pop = ([head, ...tail]) => [head, tail]
// ------------------------------------------------------------
// stackManip, version 1.
// The passing of state through computations is tedious
// and errorphrone. We can do better. Read on.
const stackManip1 = stack => {
const [first, stack1] = pop(stack)
const [second, stack2] = pop(stack1)
const [_, stack3] = push(first * second)(stack2)
const [__, stack4] = push(99)(stack3)
return pop(stack4)
}
console.log("stackManip1:", stackManip1([3,2,1])) // [99, [6,1]]
// ------------------------------------------------------------
// Use chaining
// Chain a stateful computation with a function returning a
// stateful computation, the result being a stateful computation.
// Using the name 'chain', taken from Fantasyland,
// https://github.com/fantasyland/fantasy-land.
// Similar functions is also known as bind and flatMap.
var chainComputation = (h, f) =>
state => {
const [a, newState] = h(state)
return f(a)(newState)
}
// stackManip, version 2, based on chained computations.
// The state is not part of the definition, which is good,
// but the implementation is even more messy than stackManip
// version 1. Read on...
var stackManip2 =
chainComputation(
pop,
first => chainComputation(
pop,
second => chainComputation(
push(first*second),
() => chainComputation(
push(99),
() => pop))))
console.log("stackManip2:", stackManip2([3,2,1])) // [99, [6,1]]
// ------------------------------------------------------------
// Using a for-comprehension simulation
// A simulation of a for-comprehension using es6 generators.
// Here, we traverse the computations step by step, chaining
// the steps.
// (We could easily extend this function by adding the chain
// method as a parameter, so it can be used by other chainable
// structures)
// Taken from https://curiosity-driven.org/monads-in-javascript
var forC = stepsGenerator => {
const step = stepsGenerator()
const nextStep = previousResult => {
const {done, value} = step.next(previousResult)
return done ? value : chainComputation(value, nextStep)
}
return nextStep()
}
// And now we are approaching nirvana. There are no references
// to stack in the implementation.
// (Although the generator stuff still makes is look a bit messy)
var stackManip3 =
forC(function*() {
const first = yield pop
const second = yield pop
yield push(first * second)
yield push(99)
return pop
})
console.log("stackManip3:", stackManip3([3,2,1])) // [99, [6,1]]
// ------------------------------------------------------------
// Full nirvana.
// Sorry, javascript is not there yet. But maybe ES9 or 10 will
// include a for-comprehension, and then it might look like this:
// var nirvanaStackManip = for* {
// const first = yield pop
// const second = yield pop
// yield push(first * second)
// yield push(99)
// return pop
// }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment