Reputation: 4620
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
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
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
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