I've been reading around lately about functional monads written in imperative languages, then I've tried to bring them to Vala. I'm not going to explain what a monad is, there's a lot of papers out there.
If you want to test the code below you need Vala git master (more recent than 0.9.2).
First of all we need a basic framework and some monads you can immediately read here.
Notice some comments are in do notation and that related functions are really only utilities to let the example code below be more readable, they respect the functional semantics of monads without adding more imperative power.
The only difference with other implementations is that BindFunc not only passes the underlying value but also the return function. This is helpful to avoid keeping monads around in termporary variables.
Now let's port some Haskell typic examples to Vala:
safe_div a b = if b == 0 then Nothing else Just (div a b)
Monad safe_div (int a, int b) { if (b == 0) return nothing; return new Just (a/b); }
The nothing variable is initialized with Nothing.instance which is a singleton.
Now let's map a simple multiplication function:
bind_and_mul monad factor = do { x <- monad; return (x*factor); }
Monad bind_and_mul (Monad monad, double factor) {
return monad.bind ((v,unit) => unit (((int)v) * factor)) as Monad;
}
Pretty easy, we get the unpacked value and multiply it with the given value.
Things get a bit longer with list monads. We want that given an x the function returns [x,x+1]:
bind_and_list_plus monad val = do { x <- monad; return [x,x+1]; }
Monad bind_and_list_plus (Monad monad, int val) {
return monad.bind ((x,unit) => {
var l = new List (); l.add (x); l.add (((int)x)+val); return unit(l);
});
}
Ok, but what is the result of those weird definitions?
Haskell: do { x <- safe_div 5 2; return (x*2); }
Vala: bind_and_mul (safe_div(5,2), 2);
Result: Just (4)
What if we divide by zero?
do { x <- safe_div 5 0; return (x*2); }
bind_and_mul (safe_div(5,0), 2);
Result: Nothing
Transform -10 into some useless list:
do { x <- -10; return [x,x+1]; }
bind_and_list_plus (new Just (-10), 1);
Result: [-10,-9]
Now do the same transformation for a list:
Haskell: do { x <- [1,2,3]; return [x,x+2]; }
Vala: var l = new List(); l.add(1); l.add(2); l.add(3);
bind_and_list_plus (l, 2);
Result: [[1,3],[2,4],[3,5]]
Again, the complete code with test cases can be found here.
I thought monad was introduced in haskell to allow "imperative" style. So, to see monad in imperative languages is rather surprising.
ReplyDeleteSame here. Does it actually allow to do something easier?
ReplyDeleteWhat are the benefits? Bringing monads to Vala just for the sake of allowing some kind of hybrid "functional-style imperative programming" is just plain useless in my opinion.
Yes Jiří, I can't think of anything useful at the moment. But yes, for an imperative language, they are quite useless, while in functional languages they're even used for concurrent programming.
ReplyDeleteIt was just a sort of test to see if Vala would have allowed that (in fact I've discovered a couple of bugs ;) ).
Nor Jaidi I was even more surprised seeing monads in C code before I actually wrote the post.
ReplyDelete