> The outermost monad (rightmost bound function) is in the driver seat,
and is absolutely free to ignore the monad to its
> left (which in turn encloses monads to its left)! This includes of
> course the main input IO () monad. Don't believe me?
>
> Prelude> const 3 (return "Nuclear waste leaking..." >>= print) + 5
> 8
>
> Phew, no nuclear waste leaked after all. What a relief!
No, I don't believe you.
It's true that the right monad instance can ignore its left sibling
(same monad of course, just a different instance) to allow backtracking
(the Const monad being a trivial example of this), but IO is not such a
monad.
Your example just shows that monad instance expressions (like any
Haskell expression) are evaluated lazily in Haskell.
I am relieved though, that no nuclear waste was leaked by your example.
Dan Weston wrote:
Here's my rant about the way monads are explained in Haskell tutorials
(which are much too polluted with nuclear waste to be safely approached):
It is a big mistake to start with the IO monad. It pollutes and
misdirects the understanding of what a monad is. The dreaded "nuclear
waste" metaphor is actually a red herring, having nothing do do with a
monad at all, merely the artifact of the absence of an exit function in
the IO monad (which actually does exist and is called unsafePerformIO,
but that is a secret. Shhh...), combined with Haskell's refusal to work
for free.
Monads are required to have an extrance (reification/constructor
function) called return. They are *not* required to have an exit
(reflection/deconstructor function).
They *do* combine (bind) together like links in a chain, which is not a
list but an equivalence class of trees, where the only thing that
matters is the order of the leaves, so unlike a list you don't have to
start at the tail and assemble one-by-one. [Actually, the compiler picks
just one of these trees (the singly-linked list) but expects that all
trees would evaluate the same.]
They *do* bind only with flavors of themselves (State a cannot bind to
IO b, though a monad transformer can merge the two). The output type of
one monad instance must match the input type of another monad
reification function (e.g. return) that it wants to bind to.
In compensation for this tribalism, those snobby monads that want to
clique off are out of luck: a monad cannot restrict what possible types
can be used to construct a monad instance. That would be discrimination,
and you must agree to accept all comers.
Simple, no? Apparently not...
Other things have nothing to do with monads per se gunk up the
descriptions about monads:
One red herring (the nuclear waste metaphor) refers to the fact since
monads may or may not have an escape-clause (called variously runXXX,
evalXXX, unsafePerformXXX), and IO in particular does not. The presence
or absence of this has *nothing* to do with monads (only with IO),
participates in no monadic laws, and shouldn't even be in the chapter on
Monads. Whether nuclear waste seeps in (as in a State monad) or stays
out (IO monad) has nothing to do with their monadic property and is a
special property of those particular classes.
Another even redder herring is the dreaded sequencing aspect. Monads do
sequence *denotationally*, the way any nested closures sequence, which
is exactly *backwards* from the naive understanding of sequencing:
symbols defined to the left are in the *inner* scope, those to the right
are in the *outer* scope. Perversely, when the symbols are evaluated,
the rightmost monad is evaluated first. The leftmost monad in the
program, the IO () passed in by main, is the *last* thing to be
evaluated, not the first. The outermost monad (rightmost bound function)
is in the driver seat, and is absolutely free to ignore the monad to its
left (which in turn encloses monads to its left)! This includes of
course the main input IO () monad. Don't believe me?
Prelude> const 3 (return "Nuclear waste leaking..." >>= print) + 5
8
Phew, no nuclear waste leaked after all. What a relief!
This sequencing then has nothing to do with *operational* sequencing.
When the symbols are evaluated is the basic call-by-need data-dependent
stuff of most Haskell symbols and again, has nothing to do with monads.
I learned about the IO monad first and I regret it bitterly. It cost me
a years' worth of misunderstanding. I misapprehended that a monad had
something to do with nuclear waste and never escaping the
single-threadedness. I hope the new O'Reilly book doesn't make that
mistake. Teach IO right away, but just don't call it a monad. IO is the
exception, not the rule, in the menagerie of Haskell monads.
How does all this map to C++? A monad is a a class, with no useful
interface for the end user, that looks roughly (i.e. I haven't tested
it) like:
template <class M, class T>
class Monad
{
public:
virtual ~Monad() {}
// return
Monad(T t) : t_(t) {}
// bind operator (>>=), where
// F :: Monad<M,T> -> (T -> Monad<M,U>) -> Monad<M,U>
virtual template <class U> Monad<M,U>
operator>>(typename U::F f) = 0;
private:
T t_;
};
C++ programmers will immediately see past the syntactic kruft to notice
that
1) the constructor arg is not a value but an unevaluated function
object, that starts out unevaluated.
2) The result of m >> f is a monad object, totally unevaluated.
3) There is no member function to do anything with the monad at all! As
is, it is useless.
Derivers of this class will naturally want to add such functionality:
template <class M, class T>
class MyMonad : public Monad<M,T>
{
// The parent class needs to know what type of monad
// this can bind to
typedef someUnaryFunctionObjectTypeReturningB F;
// There is no input, just an output!
// The input is via the constructor arg of the innermost monad
B operator()() { ... start the ball rolling ... }
};
Non-C++ programmers will be stunned that the above garbage can be
understood by anyone, compared to the simplicity of type classes in
Haskell. When all you have is a hammer...
The moral of the story is that monads are less than meets the eye. You
can create them and concatenate them, but may not be able to do anything
with them (some monads do let you, some don't), though the runtime
system agrees to evaluate one special monad exactly once.
What the rightmost monad does with its internals (including all
inner/left monads bound to it) has nothing to do with monads whatever,
except for the minimal requirement that they agree to be bound only to
other monads like themselves, and that as a group they all agree to not
form a clique (i.e. they are associative).
What could be simpler that that? No please, no more nuclear waste! :)
Dan
peterv wrote:
Kaveh> "A monad is like a loop that can run a new function against its
variable in each iteration."
I’m an imperative programmer learning Haskell, so I’m a newbie, but
I’ll give it a try ☺ Making mistakes is the best way to learn it ;)
There are lots of different kinds of monads, but let’s stick to the IO
monad first, which you seem to refer to.
No *an IO monad is not a loop at all*. Instead, from an imperative
programmer’s point of view, the following might be better:
“an IO monad is a delayed action that will be executed as soon as that
action is needed for further evaluation of the program.”
The simple program
main = getLine >>= putStrLn
can be visually represented as (see attachment)
The “world” (=a representation of your computer’s hardware) is passed
to the main function, which passes it to all actions that it
encounters during its lazy evaluation, causing the executing of the
actions as an effect.
The red wire through which the “world flows” is a “single thread”, it
cannot be split (because the physical world cannot be copied!!!), so
no unwanted side effects can ever occur, making IO safe in Haskell.
When you write your IO program, this world object is never available
(the IO type is a special internal type), so the red wire is erased
from the diagram, and the getLine and putStrLn boxes become “delayed
actions”.
Imperative programmers like myself might initially be confused when
they see Haskell’s do notation, because it looks like the actions are
strict statements as in C/C++/Pascal/Java/C#/etc, but they are not.
For example, try the following program:
main = do last [
putStrLn "NOT executed although it is first in the list, as
it is not used by the main function!",
putStrLn "This action IS executed because it is evaluated
by the main function." ]
This is of course all due to Haskell’s laziness which only evaluates
just those expressions that it needs to evaluate the main function.
One thing to note in the diagram above is that the getLine box has TWO
outputs, the String and the World. But functions can only have a
single output, but this can be tuple. Hence the passing of the world
from one box to the other is a bit more complicated. It is this
pattern of extracting both values from the output and passing them to
the next function and other related combinations that form the generic
monad class, which can be used for many more things than IO.
See http://haskell.org/haskellwiki/IO_inside for a much deeper and
more correct explanation ☺
And for the pros here, did this newbie make any sense? Probably not ;-)
Oh no, yet another monad explanation!!! Now the universe will most
certainly collapse…
No virus found in this outgoing message.
Checked by AVG Free Edition. Version: 7.5.476 / Virus Database:
269.11.0/929 - Release Date: 31/07/2007 17:26
------------------------------------------------------------------------
------------------------------------------------------------------------
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe