FW: RE [Haskell-cafe] Monad Description For Imperative Programmer

Dan Weston westondan at imageworks.com
Wed Aug 1 16:32:53 EDT 2007


 > 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 at haskell.org
>> http://www.haskell.org/mailman/listinfo/haskell-cafe
> 
> 




More information about the Haskell-Cafe mailing list