Created
December 19, 2012 18:28
-
-
Save JackNova/4339141 to your computer and use it in GitHub Desktop.
Monads Implementation in javascript, as seen in crockford presentation at yui conf 2012
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
//BASIC PIECES, 3 functions: unit, bind and the bind argument | |
//function unit(value) | |
//function bind(monad, function(value)) | |
//all three functions return a monad | |
/* The unit function is a constructor (returns a monad object) | |
* The magic is in the bind function | |
* | |
* There are AXIOMS: | |
* bind(unit(value)), f) === f(value) | |
* bind(monad, unit) === monad | |
* bind(bind(monad, f), g) === bind(monad, function (value) { return bind(f(value), g); }) | |
*/ | |
//to avoid defining lots of bind function for different kind of monads | |
//and manage naming interference | |
//we turn the function into a method | |
//to define many different monads with similar properties | |
//various languages like common lisp have a macro system. | |
//javascript does not but does have functions and dynamic objects | |
//so you can get MACROIDS | |
function MONAD() { | |
return function unit(value) { | |
var monad = Object.create(null); | |
monad.bind = function (func) { | |
return func(value); | |
}; | |
return monad; | |
}; | |
} | |
//returns a unit function (unit function return in Haskell) | |
//it creates a monad that inherits nothing | |
//it puts a bind method in that monad, and the bind method | |
//will take a function as an argument and return the value of calling that function with the value | |
//that was passed in to the unit constructor. Then it returns a monad. | |
//in this for it is called the IDENTITY MONAD: the value that got passed into the unit | |
//will be the thing that gets passed into all the bind functions. | |
var identity = MONAD(); | |
var monad = identity("Hello world"); | |
monad.bind(alert); | |
//let's rewrite axioms in the object oriented form, in the method form. | |
unit(value).bind(f) === f(value) | |
monad.bind(unit) === monad | |
monad.bind(f).bind(g) === monad.bind(function (value) { | |
return f(value).bind(g); | |
}) | |
//let's compare something: | |
bind(bind(monad, f), g) | |
monad.bind(f).bind(g) //Ajax Monad | |
//from | |
monad.bind(func) | |
//to | |
monad.method() | |
monad.bind(func, [a, b, c]) //second argument who will optionally take an array of parameters | |
monad.method(a, b, c) | |
//MACROID MODIFICATIONS | |
function MONAD() { | |
var prototype = Object.create(null); | |
function unit(value) { | |
var monad = Object.create(prototype); | |
monad.bind = function (func, args) { | |
return func.apply(undefined, //this is bad, in the next edition of javascript ES6 we will probably have | |
[value].concat(Array.prototype //a method called SPAT that will simplify things | |
.slice.apply(args || []))); // -> return func(value, ...args) | |
}; | |
return monad; | |
}; | |
} | |
function MONAD() { | |
var prototype = Object.create(null); | |
function unit(value) { | |
var monad = Object.create(prototype); | |
monad.bind = function (func, args) { | |
return func(value, ...args); | |
}; | |
return monad; | |
} | |
unit.method = function (name, func) { | |
prototype[name] = func; | |
return unit; | |
}; | |
} | |
//OR BETTER | |
function MONAD() { | |
var prototype = Object.create(null); | |
function unit(value) { | |
var monad = Object.create(prototype); | |
monad.bind = function (func, args) { | |
return func(value, ...args); | |
}; | |
return monad; | |
} | |
//this doesen't take only a function that knows about monads but any function | |
//and guaratees that the result is a monad | |
unit.lift = function (name, func) { | |
prototype[name] = function (...args) { | |
return unit(this.bind(func, args)); | |
} | |
return unit; | |
}; | |
return unit; | |
} | |
var ajax = MONAD() | |
.lift('alert', alert); | |
var monad = ajax("Hello world."); | |
monad.alert(); | |
//the MAYBE MONAD | |
//similar to NaN | |
function MONAD(modifier) { | |
var prototype = Object.create(null); | |
function unit(value) { | |
var monad = Object.create(prototype); | |
monad.bind = function (func, args) { | |
return func(value, ...args); | |
}; | |
if (typeof modifier === 'function') { | |
modifier(monad, value); | |
} | |
return monad; | |
} | |
} | |
var maybe = MONAD(function (monad, value) { | |
if (value === null || value === undefined) { | |
monad.is_null = true; | |
monad.bind = function () { | |
return monad; | |
}; | |
} | |
}); | |
var monad = maybe(null); //nothing happens | |
monad.bind(alert); | |
//PROMISES | |
//inspired from FUTURES from the ACTOR MODEL | |
//resolve the problems of TURN BASED SYSTEMS (nested callbacks) | |
//a promise is an object that represents a possible future value. | |
//every promise has a corresponding resolver that is used to ultimately assign value to the promise | |
//a promise can have one of three states: kept, broken or pending | |
//a promise can accept functions that will be called with th evalue once the promise has been kept or broken. | |
//promise.when(success, failure) returns another promise for the result of your success function. | |
var my_vow = VOW.make(); | |
.keep(value) | |
.break(reason) | |
.promise | |
.when(kept, broken) | |
//FILESYSTEM API | |
read_file(name) | |
.when(function success(string) { | |
... | |
}, failure) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Dario, amico mio. Sto provando ad andare OLTRE quanto detto da Doug, creandomi delle mie applicazioni di questo benedetto macroide. Solo provare ad estendere questa "ajax monad" mi fa venire i capogiri. Ti va di unire gli sforzi?
https://github.com/Muzietto/monad
Comunque buona giornata.