[Haskell-cafe] The functional-object style seems to be gaining momentum.

Alexander Solla ajs at 2piix.com
Fri Jun 18 17:36:14 EDT 2010


On Jun 17, 2010, at 10:47 AM, caseyh at istar.ca wrote:

> The functional-object style seems to be gaining momentum.
> Is there any way to convert monads into objects, so that beginners  
> have an easier time with the syntax and thus we can attract more  
> people to the language?

I think you're a little bit confused about monads.  When you define a  
monad instance, all you are doing is defining the syntax that lets you  
chain properly typed computations together.  (>>=) does almost exactly  
what (->) does in object oriented Perl, and what (.) does in Ruby and  
Java.

Consider something like:

1.plus(10).minus(5).square.log
return 1 >>= return . (+10) >>= return . (-5) >>= return . square >>=  
return . log

They both bind the "return value" of a computation (or chain of  
computations) to the next function in the chain.  return kind of does  
the opposite.  It takes a value that can be "in" the monadic action/ 
object and gives/returns a monadic action/object that contains it.

So what are the differences between the two examples?  First, and most  
obvious, Haskell's is a bit more verbose, because it explicitly uses  
return to set the right context, whereas the ruby/python/whatever  
version does it implicitly.  On the other hand, if we can abstract the  
(return .) function away, with liftM:

return 1 >>= liftM (+10) >>= liftM (-5) >>= liftM square >>= liftM log

Indeed, if we really really want, we can define "methods" for the  
monadic action, just to make it look like Python/Ruby.  (Actually,  
this is probably the preferred way to build up complex computations,  
though it is kind of silly to do for the arithmetic operators)

plusM :: (Monad m, Num n) => n -> m n
plusM n = liftM (+n)
...
squareM :: (Monad m, Num n) => n -> m n
squareM = liftM (^2)

logM :: (Monad m, Num n) => n -> m n   -- for exposition, I am  
assuming log is a part of the Num typeclass.
logM = liftM (log)		       -- I guess it's really in Rational

Suddenly, we have something that looks a LOT like the object oriented  
version:

return 1 >>= plusM 10 >>= minusM 5 >>= squareM >>= logM

In fact, this is (almost) exactly like how Perl defines its object  
methods.  The first argument of any function/procedure is assumed to  
be the object on which the method acts, and the dereferencing operator  
(->) "knows" to bind the last returned value to the first free  
variable.  The difference is that Perl objects "know" to which class  
they belong, so they can resolve compile time ambiguity at runtime (if  
the program is properly typed, which Perl can't validate at "compile  
time"...).  Since 1 is a literal in Perl, we would have to make a  
Number class and instantiate a Number object to get this to run:

Package Number;
sub new { my $class = shift; my $self = shift; bless $self, $class;  
return $self; }
sub plus arg { return $self + arg; }
sub minus arg { return $self - arg }
...

so that the computation we have been comparing turns out as:

(new Number 1) -> plus 10 -> minus 5 -> square -> log;

Indeed, we are "stuck" in the number class, and (in principle) can't  
get out without a properly typed function.   (It's also kind of  
interesting that the constructor method literally includes the snippet  
"return $self", which is almost equivalent to return 1, when you bind  
1 to (new Number).  That is, the Number constructor takes a "regular"  
value and turns it into a Number, just as return takes a value and  
wraps it in a monadic type.

The difference is that 1 isn't "blessed" into the Number class if you  
just do

return 1 -> plus 10 -> minus 5 -> square -> log

in Perl, so the dereferencing operator (->) will fail.  You need the  
"bless $self, $class" machinery that the constructor runs.  In  
comparison, Haskell's return operator polymorphically "blesses" into  
any monadic type, in virtue of its type signature return :: (Monad m)  
=> a -> m a), so that the bind operator (>>=) will never fail.

The thing that makes object orientation special isn't the syntax.   
It's the relationship objects and classes have to one another -- and  
how classes relate to each other.  Depending on your perspective,  
there is a unique largest or smallest class to which an object  
belongs.  That relationship is a monadic adjunction.  So, in  
particular, you can make a type-unsafe object system in Haskell by  
relating a "class" (for example: a named list of methods) to a type or  
value.  If you want type safety, you need to use type arithmetic to  
implement this monadic adjunction at compile time.  Somebody else  
mentioned OO Haskell.

Finally, if you want to put this all very abstractly, but in nice  
common language, an object and its corresponding monadic action are  
both "servers".  Bind, dereferencing, etc are the interfaces that can  
make a request from "ANY" server.  So the State Monad is a server that  
serves/changes the current state.  The list monad is the server that  
serves up each element of a list, in turn, etc.  Erlang ran with this  
idea for their "distributed functional object system".
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/haskell-cafe/attachments/20100618/cd14643a/attachment.html


More information about the Haskell-Cafe mailing list