mljrg
mljrg

Reputation: 4620

Why is getLine being evaluated when its value is not required?

If Haskell is lazy, why does getLine is evaluated in both of the following cases? Being lazy I would expect that in the case of fa the getLine would not be evaluated because its result is not being used subsequently:

let fa = do {
  x <- getLine;
  return "hello"
}

let fb = do {
  x <- getLine;
  return $ x
}

(I tested both cases in GHCi)

Thanks

Upvotes: 1

Views: 229

Answers (3)

MathematicalOrchid
MathematicalOrchid

Reputation: 62838

Be careful now... ;-)

The result of getLine isn't a string, it's an "I/O command object", if you will. The code actually desugars to

getLine >>= (\ x -> return "hello")

The >>= operator constructs a new I/O command object out of an existing one and a function... OK, that's a bit much to wrap your mind around. The important thing is, the I/O action gets executed (because of the implementation of >>= for IO), but its result doesn't necessarily get evaluated (because of laziness).

So let's look at the implementation of the IO monad... erm, actually you know what? Let's not. (It's deep magic, hard-wired into the compiler, and as such it's implementation-specific.) But this phenomenon isn't unique to IO by any means. Let's look at the Maybe monad:

instance Monad Maybe where
  mx >>= f =
    case mx of
      Nothing -> Nothing
      Just  x -> f x

  return x = Just x

So if I do something like

do
  x <- foobar
  return "hello"

Will x get evaluated? Let's look. It desugars to:

foobar >>= (\ x -> return "hello")

then this becomes

case foobar of
  Nothing -> Nothing
  Just  x -> Just "hello"

As you can see, foobar is clearly going to be evaluated, because we need to know whether the result is Nothing or Just. But the actual x won't be evaluated, because nothing looks at it.

It's kind of the same way that length evaluates the list nodes, but not the list elements they point to.

Upvotes: 2

bheklilr
bheklilr

Reputation: 54058

Its result is being used, just not in the way you necessarily expect. This de-sugars to

fa = getLine >>= (\x -> return "hello")

So the result of getLine is still passed to the function \x -> return "hello". Monads are inherently about sequencing actions together (among other things); that sequencing still occurs even if results are later not used. If that weren't the case, then

main = do
    print "Hello"
    print "World"

wouldn't do anything as a program, since the results of both calls to print aren't being used.

Upvotes: 4

leftaroundabout
leftaroundabout

Reputation: 120711

Congratulations, you've just discovered why Haskell is a pure functional language!

The result of getLine is not a string. It's an IO action which happens to “produce” a string. That string is indeed not evaluated, but the action itself is (since it turns up in a do block bound to main), and this is all that matters as far as side-effects are concerned.


Really just the value of getLine. This is not a function, so it doesn't actually have a result.

Upvotes: 3

Related Questions