Skip to content

Instantly share code, notes, and snippets.

@christopherscott
Last active December 14, 2015 02:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save christopherscott/5017707 to your computer and use it in GitHub Desktop.
Save christopherscott/5017707 to your computer and use it in GitHub Desktop.
The Walrus And The ECMAScripter
// 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