Live data from Hacker News

IO Monad Considered Harmful

blog.jle.im

1–10 of 75 posts

Re: IO Monad Considered Harmful

#2
I wish the author had explained what the difference between using the IO type and using the IO type via the monadic interface looked like. As a Haskell beginner, this article just makes me more confused. Can someone share an example or clarify?

Re: IO Monad Considered Harmful

#3
I can definitely say that my short sidequest to Haskell began and ended with trying to figure out what a monad was, and concluding that this was a silly first topic as well. I am glad I wasn't the only one.

Re: IO Monad Considered Harmful

#4
post #2

I wish the author had explained what the difference between using the IO type and using the IO type via the monadic interface looked like. As a Haskell beginner, this article just makes me more confused. Can someone share an example or clarify?

The IO type can be constructed in all kinds of ways. For instance, using `putStrLn` is probably the most common

    putStrLn "hello world" :: IO ()
As IO is an instance of Functor we can use that interface, for instance

    fmap (const 1) (putStrLn "hello world") :: IO Int
That would be using IO's functorial interface. As an extension to a functorial interface we also have a monadic interface. This affords us a "most boring" way of creating new IO values

    -- very different from putStrLn
    -- it doesn't actually represent *doing* anything at all
    return "hello world" :: IO String
and also, finally, a way at getting at values "inside" of IO and therefore "compose" values of IO

    -- getLine :: IO String

    capitalizer :: IO ()
    capitalizer = getLine >>= (\line -> putStrLn (map toUpper line))
So, the emphasis is that IO is "just" another type. One shouldn't refer to "the IO monad" as anything privileged unless you're actively referring to using (return, >>=) with IO. Generally "the IO monad" explains very little of IO's power as "being a monad" is a very small part of any type's interface.

---

To be clear, here are some other interfaces we can use from IO (besides just other IO value constructors)

* combining IO with exception handlers with

    catch :: IO a -> (e -> IO a) -> IO a
* forking new threads with

    forkIO :: IO () -> IO ThreadId
* embedding IO into another type with

    -- extremely common
    liftIO :: MonadIO m => IO a -> m a
* converting an IO action into an asynchronous action with

    async :: IO a -> IO (Async a)
* assigning a IO action pointed at by a finalizer to a memory pointer with

    newForeignPtr :: FunPtr (Ptr a -> IO ()) -> Ptr a -> IO (ForeignPtr a)

Re: IO Monad Considered Harmful

#5
post #2

I wish the author had explained what the difference between using the IO type and using the IO type via the monadic interface looked like. As a Haskell beginner, this article just makes me more confused. Can someone share an example or clarify?

Yes.

Here's a function that has type `IO ()` but doesn't obviously use `IO`'s monadic interface.

    foo = print "hello"
`print` may use `IO`'s monadic interface under the hood, I don't know.

Here's some code that has type `IO ()` and does use `IO`'s monadic interface.

   foo = do
       str 
which is de-sugared to

    foo = getLine >>= \str -> print str
which you can, if you like concise code, feel free to shorten to

    foo = getLine >>= print
The important part is `>>=` (pronounced "bind"), which is defined in the monad typeclass. (A typeclass is like a Java interface, by the way.)

Re: IO Monad Considered Harmful

#6
post #5
post #2

I wish the author had explained what the difference between using the IO type and using the IO type via the monadic interface looked like. As a Haskell beginner, this article just makes me more confused. Can someone share an example or clarify?

Yes. Here's a function that has type `IO ()` but doesn't obviously use `IO`'s monadic interface. foo = print "hello" `print` may use `IO`'s monadic interface under the hood, I don't know. Here's some code that has type `IO ()` and does use `IO`'s monadic interface. foo = do str which is de-sugared to foo = getLine >>= \str -> print str which you can, if you like concise code, feel free to shorten to foo = getLine >>=…

Or:

    foo = print =
which often reads better when trying to build something that looks more like a nested function call and less like a pipeline.

Re: IO Monad Considered Harmful

#7
post #2

I wish the author had explained what the difference between using the IO type and using the IO type via the monadic interface looked like. As a Haskell beginner, this article just makes me more confused. Can someone share an example or clarify?

(Also a Haskell beginner)

I think his point is that the IO type may be an example of a monad, but shut up about the monad part because it's not relevant to learning how to use it.

Re: IO Monad Considered Harmful

#8
post #2

I wish the author had explained what the difference between using the IO type and using the IO type via the monadic interface looked like. As a Haskell beginner, this article just makes me more confused. Can someone share an example or clarify?

non-monadic:

    import Data.Char

    main = (fmap.fmap) toUpper getLine
monadic (but still doesn't utilize extra power granted by monadic interface, since it produces the same result as above):

    import Data.Char

    main = do
      x 

Re: IO Monad Considered Harmful

#9
Here's how I like to explain Monads to people. I've been told it's a decent explanation. (It ignores the monad laws and such, but it's a decent conceptual overview.)

OK, so you know Java interfaces? Haskell has something just like that. They're called "type classes", though. But it works pretty much the same way. When you write out a class definition, you write A) the name of the class B) all the functions the class has to support and C) the types of those functions. (Like a Java interface definition.)

Haskell has a type class called "Monad". To "implement the monad interface", in Java parlance, you have to implement two functions. The first is called "bind", but it's written ">>=". Here's its definition:

    >>= :: m a -> (a -> m b) -> m b
Let's say our Monad was called `Foo`. In Java-land, this means the function takes a `Foo`, a function that takes an `a` and returns a `Foo`, and it uses those two things to return a `Foo`.

The other function is "return", which is defined as

    return :: a -> m a
Which, in Java terminology, means it takes an `a` and returns a `Foo`.

If you implement those two functions, you now have a Monad.

If you've been told that Monads do anything in particular, you've been told wrong. Monads don't necessarily have anything to do with IO, or sequencing, or state, or anything like that.

It just so happens that those two functions (">>=" and "return") are super useful for representing a whole lot of common things, which happen to include things like IO and stateful algorithms. Monads don't have to do either of those things, but they certainly can.

Re: IO Monad Considered Harmful

#10
post #2

I wish the author had explained what the difference between using the IO type and using the IO type via the monadic interface looked like. As a Haskell beginner, this article just makes me more confused. Can someone share an example or clarify?

If you are starting Haskell, there is a wonderful little function you can use called `interact`, which has a type `(String -> String) -> IO ()`. This takes a String to String function, and turns it into an IO type that reads from STDIN, runs the function, and writes to STDOUT, lazily. Thus, you can just write `main = interact myFunction` and get everything you need to build complex Unix pipeline utilities without needing to worry about any impurity or composition. I suggest you stick to this until you are comfortable with the basic syntax, types, lazyness, and typeclasses of Haskell.

The problem with learning about the monad typeclass first is that it is a very very high level abstraction, and abstractions are near impossible to understand without a working understanding of examples that they might abstract.

Post reply on HN