On Jun 17, 2010, at 10:47 AM, cas...@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