Last active
August 29, 2015 14:26
-
-
Save gatlin/ae4e52f4c6804bb15d31 to your computer and use it in GitHub Desktop.
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
/** | |
* Run with node.js: | |
* | |
* $> node skiing.js | |
* | |
* For a decent overview of what S and K and the rest of all this is: | |
* | |
* https://en.wikipedia.org/wiki/SKI_combinator_calculus | |
* | |
* This is one of those things where the actual theory isn't difficult, but | |
* that means using it *is*. :) | |
*/ | |
// S and K combinators. Total magic. wow | |
var S = function (x) { return (function (y) { return (function (z) { | |
return (x(z))(y(z)); }); }); }; | |
var K = function (x) { return (function (y) { return x; }); }; | |
// I combinator, which is an identity function ( I(5) === 5 ) | |
var I = (S(K))(K); | |
// Composition of two functions | |
var compose = (S(K(S)))(K); | |
// Using these 4 functions we can define a monad[1] over unary functions. | |
// What does this monad do, though? Take these definitions for granted for now: | |
var ret = K; | |
var join = S(((S(K(S(I))))(K))); | |
var flatMap = function (ma, f) { | |
return join((compose(f))(ma)); }; | |
// This isn't strictly necessary but the code will make more sense when you | |
// read it: | |
var ask = I; | |
// The inner function will "ask" for some magic value and return whether or not | |
// it is an even number: | |
var r1 = flatMap(ask, function(n) { | |
// ^-- how does this get us a value? | |
var itIsEven = (n % 2 === 0); | |
return ret(itIsEven); }); | |
// And this function will take a boolean value indicating whether the magic | |
// number was even, and then compose a sentence using it and the magic number: | |
var r2 = function(itIsEven) { | |
return flatMap(ask, function(n) { | |
// ^-- there it is again! | |
var s = n.toString(); | |
return ret (s + (itIsEven ? " is even!" : " is odd!")); | |
}); | |
}; | |
// `flatMap` lets us stitch these two functions together: | |
var r3 = flatMap(r1, r2); | |
console.log(r1(10)); // 10 will be the magic number; returns true | |
console.log(r3(10)); // 10 will again be the magic number; | |
// returns the string "10 is even!" | |
/* | |
* So with those definitions I can write functions which have both arguments | |
* and a magic embedded value, and when I stitch them together they'll have the | |
* *same* magic value. | |
*/ | |
// convenience | |
function plus1 (x) { return x + 1; } | |
// `local` may be used to modify the magic value locally for a sub-computation; | |
// though it will be reverted when the sub-computation terminates. | |
var local = function(f,m) { return (compose(m))(f); }; | |
// This function locally modifies its magic value and then calls r1 | |
var r4 = flatMap(ask, function(n) { | |
return local(plus1, r1); }); | |
// Analogous to r3 | |
var r5 = flatMap(r4, r2); | |
console.log(r5(10)); // returns the (stupid) string "10 is odd!" | |
/* | |
[^1] Monad: In part, a thing which you can map a function over, like an | |
array. Eg: | |
[1,2,3].map(function(x) { return x * x; }) ==> [1,4,9] | |
However, a monad also lets you change the structure of the container, not just | |
the values, while mapping: | |
[1,2,3].flatMap(function(x) { return [x,x]; }) ==> [1,1,2,2,3,3] | |
In that case, I made the array twice as long. Or I could do this: | |
[1,2,3].flatMap(function(x) { return (x % 2 === 0) ? [] : [x]; }) ==> [1,3] | |
In that case I made the array shorter. The choice of the name `flatMap` comes | |
from the fact that instead of returning the new mapped value, you return a | |
whole new structure for each element, and these are "flattened" out into one. | |
I defined a whole bunch of monads (and other structures) here, and I'm | |
thinking of making it into a library: | |
https://gist.github.com/gatlin/42e32522f9c6ab808af3 | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment