>       currentSecond = second $ unsafePerformIO localDateTime
>
>       where `localDateTime' has been defined via primitive
>     call to C:
>
>       localDateTime :: IO DateTime
>
>       To my distress the clock stopped after the first call to
>       `currentSecond'. I took me much more than just few seconds
>       to realize that the problem was not related to any
>       bug in the C code, but in the signature of
>       `currentSecond':
>
>               currentSecond :: Int
>
>       This is all fine and dandy if `currentSecond' is within `where'
>       clause, because it will be always evaluated afresh.

Hi Jan,

You have discovered the essence of monads, ie the difference between the bad
and ugly world of side-effecting computations and the nice and clean world
of pure functions. And even using my favourite example (*)!

You say the currentSecond has type Int, so it is a pure value. However, when
you assume that, you can prove that True equals False. It is not without
reason that unsafePerformIO is called *unsafe*PerformIO!

Here is one way of evaluating the expression (\x -> x == x) currentSecond:

    (\x -> x == x) currentSecond
    = { if x == x is not even True, I am a broomstick }
    (\x -> True) currentSecond
    = { apply function }
    True

Here is another way of evaluating the expression (\x -> x == x)
currentSecond:

    (\x -> x == x) currentSecond
    = { apply function }
    currentSecond == currentSecond
    = { look at my watch }
    5 == currentSecond
    = { look at my watch again, I type slowly :-) }
    5 == 18
    = { oops! }
    False

You said, "To my distress the clock stopped after the first call to
`currentSecond'". Well, it is not the clock that stopped, but Haskell that
assumes that it only has to look at the clock once to compute currentSecond,
and thereafter immediately return that value everytime currentSecond is
needed. After all, Haskell is a lazy functional language. This corresponds
more or less to the following:

    (\x -> x == x) currentSecond
    = { look at watch }
    (\x -> x == x) 59
    = { no matter what order you proceed }
    True

If you want to play it rough, you could do what ML programmers do and
explicitely delay and force the computation of currentSecond:

  currentSecond :: () -> Int
  currentSecond () = second $ unsafePerformIO localDateTime

However, you should cross your fingers that the compiler is not smart enough
to do common subexepression elimination when it sees the application
currentSecond () twice!

I would urge you and all the numerous other subscribers to the Haskell
mailing list and readers of comp.lang.functional that struggle to grasp the
idea of monads, to download or buy and then study all those great papers and
books on monads that people like Euginio Moggi, Simon Peyton Jones, John
Launchbury, Phil Wadler, Graham Hutton, Richard Bird, Paul Hudak, John
Peterson, .... have written to explain the beauty and relevance of monads to
the rest of us.

And if you can explain why IO a is not the same as World -> (a,World), you
are ready to move on to John Hughes' arrows.

Yours,

Erik Meijer

(*) I also like the example that Phil Walder uses where instead of
currentSecond he uses ha = unsafePerformIO $ putStr "Ha!". This will make
you stop laughing after the first cal to ha :-)



Reply via email to