[Haskell-cafe] evaluation semantics of bind

Jake McArthur jake at pikewerks.com
Thu Feb 5 11:06:43 EST 2009

Hash: SHA1

Gregg Reynolds wrote:
| As I understand it, a monad is a kind of programming trick the uses
data dependency to force evaluation order.

Actually, a monad does nothing more than supply some combinators. These
combinators (and some trivial laws) are all you really need to know to
understand monads. Specific monads are really just a case-by-case issue.

Using some function names that are not used in the standard Haskell
libraries, here are the important functions you have access to when you
have a monad, all of which return monadic values:

~    point ::                   a -> f a
~    map   ::   (a ->   b) -> f a -> f b
~    apply :: f (a ->   b) -> f a -> f b
~    bind  ::   (a -> f b) -> f a -> f b

I aligned the type signatures the way I did so you could see their
similarities and differences more easily.

point is the same as the return and pure functions. It lifts a pure value.

map is the same as the (<$>), fmap, liftA, and liftM functions. It
applies a pure function to a lifted value.

apply is the same as the (<*>) and ap functions. It applies a lifted
function to a lifted value.

The type signature for bind is reversed from that of (>>=). It's the
same as (=<<). I think it is more clear and better parallels the other
functions I've already shown. It applies a "lifting" function to a
lifted value.

| since the value of f x depends on the value of x, the evaluator must
evaluate x before f x

This is actually not true. Remember, Haskell is a lazy language. x need
not be evaluated before applying f to it.

| However, consider:
|     getChar >>= \x -> getChar
| An optimizer can see that the result of the first getChar is discarded
and replace the entire expression with one getChar without changing the
formal semantics.

Consider how this expression would actually look in a Haskell program:

~    main :: IO ()
~    main = getChar >>= \x -> getChar

Remember, a type coincides with a value. That is, the entire main
function is a _value_ of type IO (). The value, as it so happens, is an
imperative program which the Haskell runtime performs. That is, the
reduction semantics of IO expressions _produce_, rather than perform,
actions. The actions are only performed by the runtime. Every monad has
a "run" function. The IO monad's run function is the Haskell runtime.

If you think of the IO monad as some sort of Writer monad, it is easy to
see that Haskell's reduction semantics actually guarantee correct order
of operations, and a semantics-preserving optimizer will not change it.

In GHC, the IO monad is actually more like a State monad (over
RealWorld). It is also easy to see that this preserves correct ordering
of side-effects as well. I just wanted to present a different point of view.

- - Jake
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org


More information about the Haskell-Cafe mailing list