Haskell is the language that popularized (?!) monads, but my understanding was impeded because of the myriad of new things that I needed to learn about Haskell before I could even get to monads (different syntax, purity/laziness, typing system, significant white-space, etc.) All that added up to something as clear as mud. I needed a Rosetta Stone to help me decipher this new language and its features. I decided I should transliterate a few monad programs into a language I could better understand. And I choose perl. Once I did that, then things became *much* clearer to my imperative brain. This article isn't intended to give a complete explanation of what monads are. It is a supplement to be used along with the many other monad tutorials out there. Here's hoping that this is as much a help to you as it was for me.
Monads are one method programmers in pure functional languages use to simulate side-effects (like state and I/O) which aren't normally allowed by their language. Perl doesn't have many limitations when it comes to side-effects (like variable assignment, printing, etc. (But don't press your luck by doing too much in a signal handler)). There are numerous tutorials on the 'Net, but a lot of them seem to assume a high-level mathematics background and none have used Perl as their implementation language. So let's remedy that. Monads do two separate things for us. They simulate state (allowing things like assignment [a=a+1]) and they provide an ordering to our functions (to make sure things like I/O get executed in the correct sequence).
If you squint a little bit and try not to carry the idea too far, you can think of monads as sort-of-kind-of like tied functions (if Perl actually had such a beast). When you would try to compute something with one of these tied functions (i.e. by calling it), perl would also invoke some hidden procedure which acts upon our state. (Fig. 1)
X = 1 + 1 Y = 2 * 3 Z = X + Y
Both X and Y have to be computed before Z can be evaluated. You can think of lazy functions being like file handles or sockets that block until their answer is known. Pretend that all functions X,Y and Z are executing parallel, but Z is blocked until both X and Y are available.
#use forking open to start 3 more processes unless (open X, "-|") {sleep(5); print 1+1; exit }; # X=1+1 unless (open Y, "-|") { print 2*3; exit }; # Y=2*3 unless (open Z, "-|") { print <X>+<Y>; exit }; # Z=X+Yprint "Z=\n"; print <Z>."\n";
In this example, since X and Y don't have side effects, evaluating X first vs. Y first doesn't matter. But if X had the side effect of opening a file and Y was closing a file, you'd better make sure it gets executed in the proper order. Below is the canonical example of a set of functions that have a data dependency.
#data dependency and manually passing state around #count how many times some_func gets calledAnd here is how you would normally do it with global variables in a strict language...$state0 = 0; #initial state ($a,$state1) = some_func($x, $state0); ($b,$state2) = some_func($y, $state1); ($c,$state3) = some_func($z, $state2);
sub some_func { my $state = $_[1];
blah; blah;
return (blah, $state+1); }
#Mutable state -- global variableour $state = 0;
$a = some_func($x); $b = some_func($y); $c = some_func($z);
sub some_func { blah; blah; $state++; return blah; }
We know that our functions will be executed in the proper order if thy depend on one another, but what do we do if our functions don't naturally depend on one another? We make a dependency up out of thin air.
#Create artificial dependency #assume some_func1 and some_func2 don't normally take argumentsHmmmm... Making up useless variables doesn't sound very scientific, but don't worry, they get optimized away. The first pass of the compiler will analyze the dependencies and make sure the operations get executed in the proper sequence. Then the optimizing pass will eliminate the spurious "$world" we've invented. Once we've got an execution order defined, we can execute side-effecting code all we like. Well, almost. Haskell is supposed to be referentially transparent, but what about functions like getCh? Obviously they can't be referentially transparent, right? That is correct. So Haskell provides a language primitive to deal with this. It is known as "ccall", and it takes a pointer to a C function, calls it, and returns its result. "ccall" is defined to be strict (non-lazy) and it is also flagged as being not referentially transparent, so the compiler won't perform optimizations which depend on that property. Once we've identified a need to create dependencies, we can start to recognize patterns in our code and build functions to abstract them away.$world2 = some_func1($world1); $world3 = some_func2($world2);
# "Then" factors out commonalities in the above code$s = Then(&some_func1, Then(&some_func2, &some_func3));
sub Then { ($f, $more) = @_; return sub{ my $world1 = shift; my $world2 = $f->($world1); my $world3 = $more->($world2); } }
These hidden data structures are composed of closures (also know as anonymous subroutines). Before we get too involved, let's see just what sorts of data structures we can build with closures. Below is a list made of closures.
$l = sub{(3.14, sub{(2.7, sub{(1, sub{(0,undef)})})})};
When $l is executed, it returns a pair, the first item of the pair is the next value in the list (the head), and the second item is a closure which represents the rest of the list (the tail). We can create functions which manipulate this new type of list we've just created.
sub head #returns the head of a list { (my $h, my $t)=$_[0]->() if defined($_[0]); return $h; }sub tail #returns tail of a list { (my $h, my $t)=$_[0]->() if defined($_[0]); return $t; }
sub cons #helper function to construct new lists { my $val=shift; my $list=shift; return sub { return ($val, $list) } }
With the new cons() function we could create our original list like cons(3.14, cons(2.7, cons(1, cons(0,undef)))) And here's a version of map (notice the capital 'M')...
sub Map { my $f=shift; my $list=shift; (my $head, my $tail)=$list->() if defined($list); defined($tail) ? sub{ ($f->($head),Map($f,$tail)) } : sub{ ($f->($head),undef) }; }
This is how you'd invoke it $f=Map(sub{2*$_[0]+1},$g); . We can also do infinite lists...
sub zeros #endless supply of 0's { return sub{ (0,zeros()) } }...and we can combine all this stuff...
$x=cons(3, zeros()); #a three and lots of zeros $y=Map(sub{$_[0]+2}, $x); #add two to everything
So that is a demonstration that closures can be use to store data, but how does that fit into the scheme of things? Closures are the closest thing we have to state in a pure language. In mathematical terms, you can think of it as taking an equation and binding one of its free variables to a particular value.
sub add { $_[0] + $_[1] }; sub curried_add { $arg1 = $_[0]; return sub { add($arg1, $_[0]) }; }Lambda abstraction (creating anonymous subroutines) is also more verbose in perl. Here's an example of an anonymous function that increments its argument by one.$plus5 = curried_add(5); print $plus5->(6); # eleven print curried_add(3)->(4); # seven
--Haskell y = \x -> x+1#perl $y = sub { my $x=$_[0]; return $x+1 }
Here's the simple evaluator from _Monads For Functional Programming_. It takes a string and performs divisions ("Div") of constant integers, ("Con 4"). The string "Div Con 18 Con 2" evaluates to 9, and Div Div Con 18 Con2 Con 3" evaluates to 3.
#!/usr/bin/perl -wmy $question="Div Con 18 Con 2"; my $ans=ev($question); print "ans=$ans\n";
sub ev { my $term = $_[0]; my $con=qr{Con (\d+)}; my $div; $div=qr{Div ($con|(??{$div})) ($con|(??{$div}))}; if($term =~ /^$con/) { return $1; } elsif($term=~/^$div/) { my $numer=$1; my $denom=$3; return ( ev($numer)/ev($denom) ); }else{ print "Syntax error\n"; } }
Now let's see if we can count the number of divisions performed in a monadic way. Here's some Haskell pseudo-code to demonstrate the functionality. (rename ">>=" and "return" to avoid name collision under Hugs and GHC)
main = do print (show (eval question 0)) -- the zero is the initial statedata Term = Con Int | Div Term Term deriving Show
question=Div(Div (Con 18) (Con 2)) (Con 3)
type M a = State -> (a,State) type State = Int
return :: a -> M a return a = \state -> (a,state)
(>>=) :: M a -> (a->M b) -> M b func1 >>= func2 = \initial_state -> let (a,next_state) = func1 initial_state (b,final_state) = func2 a next_state in (b,final_state)
tick :: M () tick = \x -> ((),x+1)
eval :: Term -> M Int eval (Con a) = return a eval (Div t u) = eval(t) >>= \a -> eval(u) >>= \b -> tick >>= \y -> return(a `div` b)
#!/usr/bin/perl -w use strict; use re 'eval';my $question="Div Con 18 Con 2";
my @ans=ev($question)->(0); #pass in the curried initial state print "Answer=$ans[0], Number of divisions=$ans[1]\n";
sub ev { my $term = $_[0]; my $con = qr{Con (\d+)}; my $div; $div = qr{Div ($con|(??{$div})) ($con|(??{$div}))}; if($term =~ /^$con/) { return Return($1); } elsif($term =~ /^$div/) { my $numer = $1; my $denom = $3;
return Bind(ev($numer))->( sub{ my $n=$_[0]; Bind(ev($denom))->( sub{ my $d=$_[0]; Bind(tick())->( sub{ my $e=$_[0]; Return($n/$d) }) }) }) }else{ die "Syntax Error: $term"; } }
sub Return { my $val = $_[0]; sub { my $state = $_[0]; ($val, $state) } }
sub Bind { my $func1 = $_[0]; sub { my $func2 = $_[0]; sub { my $initial_state=$_[0]; (my $a, my $next_state) = $func1->($initial_state); (my $b, my $final_state)=($func2->($a))->($next_state); ($b, $final_state) } } }
sub tick { sub{ (undef,$_[0]+1) } }
Okay, that's probably something only a mother could love. Here's a little source-filter module which provides a limited form of Haskell's "do notion". We can clean up the "ev" subroutine to look something like (full code)...
sub ev { my $term = $_[0]; if($term =~ /^$con/) { return Return($1); } elsif($term=~/^$div/) { my $numer=$1; my $denom=$3; return DO { my $n <- ev($numer); my $d <- ev($denom); my $g <- tick(); Return( $n / $d ); } }else{die "Syntax Error: $term";} }
Ah. Much nicer. Perl 6 is supposed to have currying, user defined infix operators and host of other niceties which could make monads in cleaner in that language.
Last update: 24Aug2004