Last active
December 14, 2015 02:59
-
-
Save christopherscott/5017707 to your computer and use it in GitHub Desktop.
The Walrus And The ECMAScripter
This file contains hidden or 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
// Another attempt at understanding monads by translating into my lingua franca | |
// http://www.infoq.com/presentations/Why-is-a-Monad-Like-a-Writing-Desk | |
// the return operation is prefixed with underscore because 'return' is a reserved name in ECMAScript | |
// Part 1: The door | |
// ================ | |
// Is it true that this could be a monadic return operation? | |
var _return = function(v) { | |
return function() { return v; } | |
}; | |
// Yes, because the return object is a function (container/monadic value) that when executed is "jelly" | |
_return("jelly")() // => "jelly" | |
// Is it true that this could be a monadic return operation? | |
var _return = function(v) { return v; } | |
// NO, because the return object "jelly" is not a function (container/monadic value) | |
// Is it true that this could be a monadic bind operation? | |
var bind = function(mv, f) { | |
return f(mv()); | |
} | |
var with_toast = function(s) { | |
return _return("toast & " + s); | |
} | |
bind(_return("jelly"), with_toast)(); // => "toast & jelly" | |
// YES, because it takes a monadic value and a function, | |
// applies the function to the 'value' of the monadic value | |
// and it returns a monadic value. | |
// Can you write a monadic return function for yourself? | |
_return("me"); // => [Function] | |
_return("me")(); // => "me" | |
// Can you write a function that makes you grow and returns a monadic value? | |
var grow = function(s) { | |
return _return(s + s.slice(-1)); | |
} | |
grow("me")() // => "mee" | |
// Now can you use a monadic bind operation to grow? | |
var m_grow = function(mv) { | |
return bind(mv, grow); | |
} | |
// Grow bigger!! | |
m_grow(m_grow(m_grow(m_grow(_return("me")))))() // => "meeeee" | |
// Part 2: Directions | |
// ================== | |
var directions = function(start) { | |
var next = Math.random() > 0.5 ? ", right" : ", left"; | |
return _return(start.concat(next)); | |
} | |
var m_directions = function(mv) { | |
return bind(mv, directions); | |
} | |
m_directions(m_directions(_return("here")))() // => "here, right, left" | |
m_directions(m_directions(_return(null)))() // TypeError: Cannot call method 'concat' of null | |
m_directions(m_directions(_return(undefined)))() // TypeError: Cannot call method 'concat' of undefined | |
var bind = function(mv, f) { | |
var v = mv(); | |
if (v == null) return _return(v); | |
return f(v); | |
} | |
m_directions(m_directions(_return(undefined)))() // => undefined | |
m_directions(m_directions(_return(null)))() // => null | |
// Part 3: Tea Party | |
// ================== | |
var m_tea = function(mv, name) { | |
return bind(mv, function(v){ | |
return _return(v + " and " + name); | |
}); | |
}; | |
var _return = function(v) { | |
return function(s) { | |
return [v, s]; | |
} | |
} | |
var bind = function(mv, f) { | |
return function(s) { | |
var temp = mv(s); | |
var v = temp[0]; | |
var sn = temp[1]; | |
return f(v)(sn); | |
} | |
}; | |
m_tea(_return("me"), "you")(10); // => ["me and you", 10] | |
var take_sugar = function(mv) { | |
return bind(mv, function(v) { | |
return function(s) { | |
return [v, --s]; | |
} | |
}); | |
}; | |
// (( -> (return "me") (take-sugar) (m-tea "you")) 10) | |
m_tea(take_sugar(_return("me")), 10) | |
// Epilogue | |
// ================== | |
// Identity monad | |
// -------------- | |
var _return = function(v) { | |
return function() { return v; }; | |
}; | |
var bind = function(mv, f) { | |
return f(mv()); | |
}; | |
var grow = function(str) { | |
return _return(str + str.slice(-1)); | |
} | |
var m_grow = function(w) { | |
return bind(w, grow); | |
} | |
m_grow(m_grow(m_grow(_return("me"))))() // => "meeee" | |
// Maybe monad | |
// -------------- | |
var _return = function(v) { | |
return function() { return v; }; | |
}; | |
var bind = function(mv, f) { | |
var v = mv(); | |
if (null == v) return _return(v); | |
return f(v); | |
} | |
var m_directions = function(mv) { | |
return bind(mv, function(start) { | |
var next = Math.random() > 0.5 ? ", left" : ", right"; | |
return _return(start.concat(next)); | |
}); | |
} | |
m_directions(m_directions(_return("here")))() // => "here, right, left" | |
m_directions(m_directions(_return(undefined)))() // => undefined | |
m_directions(m_directions(_return(null)))() // => null | |
// State monad | |
// -------------- | |
var _return = function(v) { | |
return function(s) { | |
return [v, s]; | |
} | |
}; | |
var bind = function(mv, f) { | |
return function(s) { | |
var temp = mv(s); | |
var v = temp[0]; | |
var sn = temp[1]; | |
return f(v)(sn); | |
} | |
}; | |
var take_sugar = function(mv) { | |
return bind(mv, function(v) { | |
return function(s) { | |
return [v, --s]; | |
} | |
}); | |
}; | |
m_tea(take_sugar(_return("me")),"you")(10) // => ["me and you", 9] | |
m_tea(take_sugar(take_sugar(take_sugar(take_sugar(_return("me"))))), "you")(10) // => ["me and you", 6] | |
// Three monad laws: | |
// 1. Left unit - "return" acts as a neutral element of bind | |
bind(_return(v) f) === f(v) | |
bind(_return("me"), grow)() === grow("me")() // => true | |
// 2. Right unit - "return" acts as a neutral element of bind | |
bind(_return("me"), _return)() === _return("me")() | |
// 3. Associative - binding two functions in succession is the same as binding one function that can be determined from them | |
bind(bind(_return("me"), grow), grow)() === bind(_return("me"), function(v) { return bind(grow(v), grow)})() // => true |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment