Live data from Hacker News

IO Monad Considered Harmful

blog.jle.im

41–50 of 75 posts

Re: IO Monad Considered Harmful

#42
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?

You don't use another interface when thinking "IO type" instead of "IO monad". It's all about what words you use to explain the types of the interface.

For example, when you write 1 + 2 you are probably thinking of (+) being addition on integers. But a mathematician could also think of (+) as the operator from the group (Z, +, 0). Groups (a concept in abstract algebra) generalize integers, but you do not need to know group theory to do arithmetic. But it's the same operator nevertheless! Just two ways of talking about it.

Similarly, when you write putStrln "Hello " >> putStrLn "world!", the "IO type" way of thinking says: (>>) is an operator that glues two IO actions together. The resulting combined action performs the argument actions sequentially from left to right. The operator has the type IO a -> IO b -> IO b.

But you can think of (>>) in more general terms. It is not meaningful only for the IO type. It is meaningful for any type that happens to be a monad! That insight may be useful eventually, but probably not for a beginner Haskell programmer just wanting to do some IO!

Re: IO Monad Considered Harmful

#43

I read the article, and it annoyed me, because the rebuttal is obvious and wasn't addressed. The rebuttal is do-notation. The second program you write after Hello World is going to use two IO actions instead of one, and so you need a way to sequence them, and every tutorial is going to do that with do-notation. And suddenly all of the other syntax you learned, like how to declare a variable with let-in, or how arrows…

The issue isn't as great as you portray it to be. You're facing beginner issues and learning a foreign topic, expect to spend some time on it and put in the mental effort and you'll master it in no time. After spending an hour to write "Guess the number" game with and without do notation, you should know enough to be able to write practical programs in Haskell. Yes, an hour for guess the number is a lot, but it's the learning part that takes the time, not just putting a program together.

It makes perfect sense to have do notation and desugaring it to use >>=. For "imperative" programming, do notation makes sense. Other times you'll be writing data processing "pipelines" where using >>= is more convenient. To give an example:

    readQueries inFile >>= readFromDb db >>= writeOutput outFile
The above would be very clumsy to write in do notation (but not too difficult, that's what you'd do in an imperative language). But writing "guess the number" without do notation is also clumsy (but a good learning exercise).

do notation and monad laws may be a foreign concept when coming from an imperative programming background, but it's also a neat an elegant concept that works very well in practice.

Re: IO Monad Considered Harmful

#44
post #30

Earlier quoted context omitted.

> Here's how I might write that today, without using do notation: `getLine >>= (return . (++) "Hello, ") >>= putStrLn`. Don't write point-free code here. Here's a fairly simple version: getLine >>= \name -> putStrLn ("Hello, " ++ name) The only novel thing there is the >>= operator itself. You don't even have to introduce return there; you can introduce return later.

Which can then be made to look exactly like the getLine >>= putStrLn case by doing greet name = putStrLn ("Hello, " ++ name) main = getLine >>= greet (Edited to include the definition of main)

If this is meant to be under main, you need a "let":

    main = do
      let greet name = putStrLn ("Hello, " ++ name)
      getLine >>= greet
For whatever reason, I didn't learn about the magic let syntax in do-notation until relatively late. Anyways this is very elegant, but not something I could have generated early on, especially because it mixes two different sequencing syntaxes (do-notation and >>=).

Re: IO Monad Considered Harmful

#45

Earlier quoted context omitted.

I think your suggestion is a relative improvement over most tutorials. But it's still unmanageably hard. "Here's how to read something and then print that: >>=". Sure, like `getLine >>= putStrLn`. Technically speaking, that's correct. But this doesn't generalize at all. Maybe I want to greet the user by name. How do I read the name, prepend "Hello, " to it, and then print that? This is a totally natural thing to want…

> Here's how I might write that today, without using do notation: `getLine >>= (return . (++) "Hello, ") >>= putStrLn`. Don't write point-free code here. Here's a fairly simple version: getLine >>= \name -> putStrLn ("Hello, " ++ name) The only novel thing there is the >>= operator itself. You don't even have to introduce return there; you can introduce return later.

You can use "fmap" to make this nicer (but that introduces yet another concept). If you use hlint on the program above, you it will tell you to use fmap instead.

    fmap ("Hello, " ++) getLine >>= putStrLn
I learned this because hlint suggested it to me. Using hlint is a very good idea, even for beginners.

For a simple "greet" program, this isn't perhaps any neater or cleaner but fmap is a powerful tool with lots of practical uses.

Re: IO Monad Considered Harmful

#46
post #32

Earlier quoted context omitted.

I think your suggestion is a relative improvement over most tutorials. But it's still unmanageably hard. "Here's how to read something and then print that: >>=". Sure, like `getLine >>= putStrLn`. Technically speaking, that's correct. But this doesn't generalize at all. Maybe I want to greet the user by name. How do I read the name, prepend "Hello, " to it, and then print that? This is a totally natural thing to want…

> getLine >>= (return . (++) "Hello, ") >>= putStrLn And my coworkers yesterday were complaining that ClojureScript looked like Brainfuck... ( http://en.wikipedia.org/wiki/Brainfuck )

That's a very contrived example you obviously didn't spend any time trying to understand. No-one in their right mind would write code like that, except to demonstrate something.

And if you did write code like that, Haskell's hlint tool would make suggestions to turn it neat and readable using fmap.

Re: IO Monad Considered Harmful

#47
post #23

I read the article, and it annoyed me, because the rebuttal is obvious and wasn't addressed. The rebuttal is do-notation. The second program you write after Hello World is going to use two IO actions instead of one, and so you need a way to sequence them, and every tutorial is going to do that with do-notation. And suddenly all of the other syntax you learned, like how to declare a variable with let-in, or how arrows…

Yes, you'll need them to build up bigger programs. The point is they aren't fundamental to IO itself.

They pretty much are as without sequencing you can only do the most trivial thing (a single IO action). Most interesting programmes are going to need at least two IO actions.

Re: IO Monad Considered Harmful

#48

I've been writing Haskell code for years, and this is one of the most singularly useful articles I've read on it. I've certainly found the proliferation of "monad tutorials" and "learn this so you can print something" guides obnoxious, but it never occurred to me that IO really could be taught completely separately from monads. Here's the outline of what the middle of a tutorial could look like, following this advice…

I think your suggestion is a relative improvement over most tutorials. But it's still unmanageably hard. "Here's how to read something and then print that: >>=". Sure, like `getLine >>= putStrLn`. Technically speaking, that's correct. But this doesn't generalize at all. Maybe I want to greet the user by name. How do I read the name, prepend "Hello, " to it, and then print that? This is a totally natural thing to want…

There's one thing that should be mentioned here: you can easily compose these things. You can do:

    echo = forever $ getLine >>= putStrLn
This also works with do notation, if you'd want to make the greet example looping.

    greetLoop = forever $ do
        name 
This is a very powerful concept. In mainstream imperative programming languages, you can't assign "statements" to variables or return them from functions or pass them as parameters.

Re: IO Monad Considered Harmful

#49

Ok, so we can't use the word monad because that's bad apparently. How do we answer "How does Haskell, a FP language where composition is a very important concept, compose IO actions?". Because that's almost always done using monadic actions. Aside, I agree on the List monad thing. I never ever use the list monad, why would I. But if I'm managing state (i.e. IO, Reader/Writer, Conduit etc), then why wouldn't I use the…

> How do we answer "How does Haskell, a FP language where composition is a very important concept, compose IO actions?".

You can answer "By using the combinators provided for composing IO actions".

The fact that the structure encoded (some of) these operations happens to correspond to a monad is something that can be left unmentioned until later.

Re: IO Monad Considered Harmful

#50
post #43

I read the article, and it annoyed me, because the rebuttal is obvious and wasn't addressed. The rebuttal is do-notation. The second program you write after Hello World is going to use two IO actions instead of one, and so you need a way to sequence them, and every tutorial is going to do that with do-notation. And suddenly all of the other syntax you learned, like how to declare a variable with let-in, or how arrows…

The issue isn't as great as you portray it to be. You're facing beginner issues and learning a foreign topic, expect to spend some time on it and put in the mental effort and you'll master it in no time. After spending an hour to write "Guess the number" game with and without do notation, you should know enough to be able to write practical programs in Haskell. Yes, an hour for guess the number is a lot, but it's the…

I have mastered it, but I have not drunk so much Kool-Aid that I have forgotten what it took!

It sure did not take "no time," and much longer than an hour - longer than any other programming language I have attempted. The biggest stumbling blocks were syntactical. For example, there are three ways to declare a variable (let-in, let, and learning was not so much new concepts but instead things like how a "not in scope" error means you used a tab instead of spaces.

I completely agree, the underlying concepts are elegant, and powerful and enlightening and quite amazing. But you can understand the concepts perfectly and still not be able to write a line of Haskell. That's where most tutorials fall short.

Post reply on HN