0
name <- getLine

The following paragraph is about how the line works. Can someone explain to me what this means? Thank you in advance.

getLine is in a sense impure because its result value is not guaranteed to be the same when performed twice. That's why it's sort of tainted with the IO type constructor and we can only get that data out in I/O code. And because I/O code is tainted too, any computation that depends on tainted I/O data will have a tainted result.

When I say tainted, I don't mean tainted in such a way that we can never use the result contained in an I/O action ever again in pure code. No, we temporarily un-taint the data inside an I/O action when we bind it to a name. When we do name <- getLine, name is just a normal string, because it represents what's inside the box.

Charana
  • 1,074
  • 1
  • 13
  • 26
  • 1
    This question is too broad. You might try another tutorial or haskell book that explains `IO` in a different way that makes more sense to you. – jberryman Oct 10 '15 at 17:57
  • 2
    The accepted answer to the question @jberryman suggested is a nicer explanation than the one in terms of taints that you quoted. To it, we might also add that when you do `name <- getLine` and then use `name` elsewhere in the do-block you are specifying what *will* be done with the value obtained through `getLine`, whatever it turns out to be, once the recipe it specifies is executed. – duplode Oct 10 '15 at 18:03
  • 3
    I will be a bit more blunt than others. The paragraph you quoted is not a good way to think about `IO`. (How would you understand `[IO a]` or `IO a -> IO b` or `IO (IO a)` in this model?) You should find a different explanation of what the `IO` type constructor means. – Reid Barton Oct 10 '15 at 18:47

1 Answers1

2

The paragraph hints to the fact that you can't use getLine in a function which has a "pure" type, without the IO monad occurring in it. E.g. if we try to run

lineLength :: Int -> Int
lineLength n = n + length getLine

the compiler will complain because length expects a String (or any other list type) but getLine is an IO String. So there's a type mismatch.

But this does not mean that length and getLine can not be composed: here's how

lineLength :: Int -> IO Int
lineLength n = do
    line <- getLine
    return (n + length line)

Above we temporarily "remove the IO" since line :: String, so that length can be applied to it, and n added to the result. However, then we are forced to use return to transform a pure Int result into an IO Int.

Notice how the IO also ends up in the function signature: this is unavoidable. In Haskell, if a function does impure things such as using IO, then the type system forces you to use IO in the type. This is the "taint" the quote is referring to: once you use an impure function/value with an IO type, then you have to use IO in the type of your own code, and any caller of that in turn will have to use IO. Impurity must be propagated in the type signatures.

This also means that if you see f :: Int -> Int, you can rely one the fact that the compiler proved the function to be pure: it will return the same result for the same input. (There are a few low-level ways to circumvent this, but they are not meant to be used in regular code.)

chi
  • 111,837
  • 3
  • 133
  • 218