Note: while the code in this question is about functional programming / monads, etc., I am not asking about functional programming (and I do not think this question should have tags related to functional programming, etc. ) Instead, I ask about using a JavaScript prototype .
Source code
I watch a Douglas Crockford video called Monads and Gonads (on YouTube or here or here ). It includes a demo implementation of the monad in JavaScript, shown below.
The monad object and its prototype
In his code, he creates a really empty object using Object.create(null) and uses it as a prototype for his possible monad object. It attaches the bind method to the monad object itself, but any user-defined functions that are later tied to the monad using lift are not tied to the monad object itself, but to its prototype.
The necessary prototype?
It seemed to me that using a prototype was unnecessary complexity. Why can't these custom functions just bind directly to the monad object itself? Then it seemed to me that a prototype would not be needed, and we could simplify the code.
Perplexed results when removing a prototype
I tried to implement this simplification and got puzzling results. Sometimes code that does not support the prototype still worked, i.e. It could still use the value of monad-wrapped (string "Hello world."), When the user-defined function was called without additional parameters ( monad2.log() ). However, when the user-defined function was called using additional parameters ( monad2.log("foo", "bar") ), the code could no longer find value , although it could still use these additional parameters.
Update on the perplexity of the results: Partly because of the answer from @amon, I realized that the cryptic results do not appear because I change the number of parameters, but because I simply repeat the call to the lift ed method on the monad (did the number of parameters change). Thus, running monad2.log() twice in a line will give the correct value the first time, but it will be undefined a second time.
Questions
So why is a prototype needed in this code? Or, alternatively, how can the elimination of the cause of the prototype value be available several times, but not at another time?
Demo Code Description
The following are two versions of the code. The prototype code ( MONAD1 ) is identical to the code used by Crockford in his video, except that the user-defined console.log function is attached instead of alert so that I can play with this in node and not in the browser. In the code that does not support the prototype ( MONAD2 ), the changes indicated in the comments have been made. The result is displayed in the comments.
Prototype Usage Code
function MONAD1() { var prototype = Object.create(null); // later removed function unit (value) { var monad = Object.create(prototype); // later moved monad.bind = function (func, ...args) { return func(value, ...args); } return monad; } unit.lift = function (name, func) { prototype[name] = function (...args) { // later changed return unit(this.bind(func, ...args)); }; return unit; }; return unit; } var ajax1 = MONAD1() .lift('log', console.log); var monad1 = ajax1("Hello world."); monad1.log(); // --> "Hello world." monad1.log("foo", "bar"); // --> "Hello world. foo bar"
Prototype-free code
function MONAD2() { // var prototype = Object.create(null); // removed var monad = Object.create(null); // new function unit (value) { // var monad = Object.create(prototype); // removed monad.bind = function (func, ...args) { return func(value, ...args); } return monad; } unit.lift = function (name, func) { monad[name] = function (...args) { // changed return unit(this.bind(func, ...args)); }; return unit; }; return unit; } var ajax2 = MONAD2() .lift('log', console.log); var monad2 = ajax2("Hello world."); monad2.log(); // --> "Hello world." ie still works monad2.log("foo", "bar"); // --> "undefined foo bar" ie ???
Jsbin
I played with this code in node, but you can see the results in this jsbin . console.log doesn't seem to work exactly the same in jsbin as it does in node in the terminal, but it still shows the same cryptic aspects of the results. (Jsbin doesn't seem to work if you just click “Run” on the console panel. Instead, you should activate the output panel by clicking the “Exit” tab, and then click “Run with js” in the “Exit” panel to see the results in the Console panel.)