Last active
October 20, 2016 21:24
-
-
Save trygvea/fe07eb066ba8c23b3600368ee61dad2d to your computer and use it in GitHub Desktop.
Pure stateful computations in Javascript (ES6), reloaded
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
// 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