/* Monads in Javascript by Cratylus in windows fire up a command shell and run cscript monad.js this is my take on what monads are, if i'm wrong let me know :) this shows how to create state transformers - ie functions which modify state and pass it along for further computation this is just to present the concept of a monad as an aid (?) to understanding ( at leasts my own ) imperative languages like javascript don't _need_ monads because they already have state built in a monad in this case is basically a function ftom a state to a pair consisting of the result of the computation and the new state : viz: monad = function from states to pairs of (result,state) so to actually grab our result of a computation, we would do finalresult = monad(startstate)[0] <---- the first element of the pair bind is a function that allows us to combine the results of one monad and feed it in to another one bind "secretly" passes the state of the computation to make bind easier to read we define two projection functions from pairs to the first and second elements of the pair mreturn just allows us to intialise a monad with a value references : ive just taken Oleg Kiselyov's scheme monads and translated them into javascript see also the paper by check out Douglas Crockford's clever javascript page too monads are actually more general mathematical entities google for monad category theory to find out more */ //start of monad.js function State(x,y) { //simple state object - imagine x and y are "registers" for example this.x = x; this.y = y; // this is just fluff this.describe = describe; } function describe() { // just fluff - change this if you're not using windows WScript.echo("x value = " + this.x.toString() + " y value = " + this.y.toString() ); } function makepair(a,s) { // just an array to hold the result and a state object return [a,s]; } function getstate(pair) { return pair[1]; } function getresult(pair) { return pair[0]; } function mreturn(a) { return function (s) { return makepair(a,s); }; } function bind(m,f) { return function (oldstate) { var pair,newstate,result,newmonad; // m is a function of a state to a pair pair = m(oldstate); // we grab the state from the pair newstate = getstate(pair); // we grab the result of the m computation which has been placed in // the result position of the pair result = getresult(pair); // we apply our function f to this result, which gives us // a new computation (i.e. a new monad : another function which takes a state to a pair ) newmonad = f(result); // finally we apply this function to the new state to get another computation return newmonad(newstate); }; } // notice that the "new" word in the makepair function -- we are actually creating a new state object each call :) var incx = function(val) { return function(state) { return makepair(null,new State(state.x + val,state.y)); }; } var incy = function(val) { return function(state) { return makepair(null,new State(state.x,state.y + val)); }; } // first we just cook up a start state var startstate = new State(0,0); // here is a simple state transformer function which adds 21 to a number, keeping the state constant function foo(val) { return function(state) { return makepair(val + 21,new State(state.x,state.y)); }; } //to illustrate the monad return law: return x >>= f = f x NB. f x is itself a state transformer function :) // mreturn just gives us a value to work with WScript.echo("Illustrating the monad return law"); WScript.echo("foo(21) in the start state") var result = foo(21)(startstate)[0]; WScript.echo(result); WScript.echo("return 21 >>= foo"); var returncomputation = bind(mreturn(21),foo); WScript.echo(returncomputation(startstate)[0]); /* we can build up complex state transformations by using bind our example corresponds to the imperative program: incx 10 incy 12 incx 100 given our state State = (0,0) we expect that the final state should be (110,12) note that we do not use the val parameter fed in to the functions here, because we are not using the results of previous computations the result null is returned by each incx or incy function , but we could make it return a value here - i'll write this up later hideous huh? */ var program = bind(bind(bind(mreturn(null),function (val) {return incx(10);}),function (val) {return incy(12);}),function (val) {return incx(100);}); WScript.echo("Illustrating monad bind:"); startstate.describe(); program(startstate)[1].describe(); // end of monad.js
Visited 15042 times