Hacker News new | ask | show | jobs
by arianvanp 4332 days ago
That is a misconceptio and is only possible if you break monad laws. Monads aren't about sequencing. Just some monads are.

    (+) <$> a <*> b 
And

    x <- a
    y <- b
    return (a  + b) 

Will yield exactly the same behaviour. Even in evaluation order. And they can be interchanged. It may look like we are sequencing in the second example but that's only true in datatype with sequencing embodied in their monad and application implementation ( State monad). An example of a datatype that doesn't have this property is [a]
3 comments

This isn't true. They are absolutely not the same and don't yield the same behavior for every monad/applicative pair.

First, you probably meant:

    do x <- a
       y <- b
       return (x + y) -- not: a + b
With the current desugaring of do-syntax there is no way the compiler will know that the statement 'b' doesn't have a dependency on the variable 'x' and will desugar to:

    a >>= \x -> b >>= \y -> return (x + y)
or, maybe more legible:

    bind a (\x -> bind b (\y -> return (x + y))
So, even if the 'b' doesn't use 'x', it is implicitly sequential and the effects of 'a' yielding 'x' need to be done before the effects of 'b' yielding 'y'. This can be a waste, e.g. when both are network calls that can be done in parallel.

The `Applicative` interfaces statically guarantees there is no data dependency and might therefor have a smarter implementation. For `Monad` this simply isn't possible. It's a subtle, but very important, difference.

Given the function "\x -> b", it is impossible for b to depend on x.
Yes, but with the same syntax you could have written '\x -> b x', changing that assumption entirely.
Even the state monad is not actually sequential. It just appears that way conceptually. It can, just like most haskell code, end up being computed in any order (because of laziness and lambda calculus reordering rules).

For the most part, only "special" monads like IO enforce sequential computation.

A good example demonstrating this is the "reverse state monad"

http://lukepalmer.wordpress.com/2008/08/10/mindfuck-the-reve...

It sends state updates "back in time" to compute fixed points. The above article shows how you can compute the fibonacci stream using it.

> For the most part, only "special" monads like IO enforce sequential computation.

No, IO doesn't either, because of laziness. If you need a demonstration, then, in the IO monad, open a file (using the functions in System.IO), read the contents (without doing anything else that depends on them), close the file, and then use the file contents.

IO still works in data dependency order.

That behavior can be seen as a perversion of IO's behavior—it's roughly known as Lazy IO and is considered convenient but potentially very dangerous.

The whole cottage industry of streaming monadic operators like enumeratees, pipes, conduits, io-streams, etc takes "fixing" Lazy IO as a major design goal or inspiration.

Lazy IO depends upon using the function `unsafeInterleaveIO` which, as its name suggests, is considered potentially unsafe---and your example demonstrates why!

You are partially correct. All IO operations "start" in sequential order. Only some of them "finish" sequentially (like putStrLn).
State is sequential when it matters

    x <- get
    put y
    z <- get
I like how the misconception is introduced by the standard library itself misnaming its own method `sequence`.
It's not entirely right to call this a misconception. The monadic bind is inherently sequential. The right hand side of the bind might compute a computation based on the result of the left hand side of the bind.
It is sequencing—but just not in "time". It sequences in "potential dependency order". This is most clear if you look at free monads.