Free Web space and hosting from freewebspace.com
Search the Web


/*
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 5073 times