[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