9

I'm trying to better understand Haskell's laziness, such as when it evaluates an argument to a function.

From this source:

But when a call to const is evaluated (that’s the situation we are interested in, here, after all), its return value is evaluated too ... This is a good general principle: a function obviously is strict in its return value, because when a function application needs to be evaluated, it needs to evaluate, in the body of the function, what gets returned. Starting from there, you can know what must be evaluated by looking at what the return value depends on invariably. Your function will be strict in these arguments, and lazy in the others.

So a function in Haskell always evaluates its own return value? If I have:

foo :: Num a => [a] -> [a]
foo [] = []
foo (_:xs) = map (* 2) xs

head (foo [1..]) -- = 4

According to the above paragraph, map (* 2) xs, must be evaluated. Intuitively, I would think that means applying the map to the entire list- resulting in an infinite loop. But, I can successfully take the head of the result. I know that : is lazy in Haskell, so does this mean that evaluating map (* 2) xs just means constructing something else that isn't fully evaluated yet?

What does it mean to evaluate a function applied to an infinite list? If the return value of a function is always evaluated when the function is evaluated, can a function ever actually return a thunk?

Edit:

bar x y = x
var = bar (product [1..]) 1

This code doesn't hang. When I create var, does it not evaluate its body? Or does it set bar to product [1..] and not evaluate that? If the latter, bar is not returning its body in WHNF, right, so did it really 'evaluate' x? How could bar be strict in x if it doesn't hang on computing product [1..]?

jub0bs
  • 60,866
  • 25
  • 183
  • 186
user2666425
  • 1,671
  • 1
  • 15
  • 21
  • 3
    Have you seen http://stackoverflow.com/questions/6872898/haskell-what-is-weak-head-normal-form ? – John L Dec 29 '14 at 07:52
  • @JohnL I had not seen that, thank you for the link. Am I right to assume then that a function returns its result in WHNF? – user2666425 Dec 29 '14 at 07:56
  • @user2666425: generally a function returns its result as a thunk (ignoring certain optimization passes). The caller of the function may force evaluation of that result in the process of computing its own return value. Evaluation generally means evaluation to WHNF. Another really great introduction to this is http://blog.ezyang.com/2011/04/the-haskell-heap/ – John L Dec 29 '14 at 08:06
  • @AndrewC I've updated my post, as I'm now more confused about what it means to evaluate something in Haskell. – user2666425 Dec 29 '14 at 08:19
  • @AndrewC `var` hangs if I ask it's value at GHCI- but loading a file containing only those two lines does not hang, and prefacing them with `let`s does not hang. I thought that was evidence that it is not evaluating the infinite list. – user2666425 Dec 29 '14 at 08:33
  • @AndrewC And, the linked article says that `foo x y = x` is strict in it's first argument, because it evaluates `x`. – user2666425 Dec 29 '14 at 08:34
  • @AndrewC If `bar` doesn't evaluate anything at all, then why does `bar (product [1..]) 1` hang? – user2666425 Dec 29 '14 at 08:42
  • Because when you type `var` in ghci, it actually runs `print var` and `print` is strict in its argument. `print var` evaluates to `print (bar (product [1..]) 1)` which evaluates to `print (product [1..])` which hangs. `print` did the evaluation, `bar` left it unchanged. – AndrewC Dec 29 '14 at 08:46
  • 2
    @AndrewC The answer (typically) is YES. That's certainly the answer for ghc. The foo function does NOT return a thunk. – augustss Dec 29 '14 at 12:09
  • @AndrewC I don't think it is, but I'll edit it if it happens to be. – Alp Mestanogullari Dec 29 '14 at 12:42
  • @augustss I've upvoted your comment because you know far more than I do about this, but I'd appreciate a little clarification of how you're interpreting "evaluate" because you can't mean full evaluation because `length (foo [undefined,undefined,undefined])` gives 2, not an error. Do you mean to WHNF? – AndrewC Dec 29 '14 at 12:50
  • @AlpMestanogullari er.... Sorry, don't follow you. What don't you think what is? What would you edit? What was your comment referring to? – AndrewC Dec 29 '14 at 12:54
  • 2
    @AndrewC When we're talking Haskell "evaluate" almost always means "evaluate to WHNF". Otherwise we'd say "evaluate fully" or something like that. – augustss Dec 29 '14 at 13:20
  • 2
    @augustss: `foo x y = x` is strict in x and (probably) wouldn't return a thunk even at `-O0`, but that's not true in general. As you state, it's necessary to look at how the return value is demanded. – John L Dec 30 '14 at 06:16
  • 2
    The function `foo x y = x` is absolutely strict in `x`. Being strict has a mathematical definition and is implementation independent. – augustss Dec 30 '14 at 06:30
  • @AndrewC I was referring to the article, I'm the author so I may give a shot at rewording some sentences if the explanations are not clear enough. – Alp Mestanogullari Dec 30 '14 at 08:44
  • @AlpMestanogullari Well you should listen to augustss, instead of to me. I've never worked on a haskell compiler. He most certainly has! – AndrewC Dec 30 '14 at 08:56
  • @AndrewC Yeah, but I'm not sure what Lennart said contradicts anything in the article, but it seems you found something there to be improvable or wrong. I would gladly fix any such thing. – Alp Mestanogullari Dec 30 '14 at 09:04
  • @AlpMestanogullari Indeed. I think my issue is that the word "evaluates" suggests full evaluation, but it's only to weak head normal form, which I feel is completely different and worth emphasising. My other issue was that this only happens when a value is being demanded of the function application itself (which is of course how lazy evaluation works) but to me it feels like the calling function is evaluating to WHNF, not the function itself. That's more of a philosophical difference than a practical difference. It's not your fault that I misunderstood you, so please accept my apologies. – AndrewC Dec 30 '14 at 09:19
  • @AlpMestanogullari And on second reading, rather than skimming, it's clear to me that your article makes both those points. :S – AndrewC Dec 30 '14 at 09:22
  • @AndrewC No apologies needed, I'm always open to feedback/improvements for this article, since it's where I tend to point people that have trouble understanding laziness/strictness on a very practical ground. I understand your confusion I think, and here's how I see it: to inspect whether a function is strict/lazy in some arguments, just assume that the calling code will evaluate the result of the function to WHNF, then just look for those values that'll have to be evaluated to WHNF (or "deeper") invariably in order for the function to be able to hand back the (evaluated) result. – Alp Mestanogullari Dec 30 '14 at 09:55
  • @AlpMestanogullari I would explain it to the previous me this way: A function is strict if `f ⊥ = ⊥`. `const` is non-strict in its 2nd argument because `const 'q' ⊥ /= ⊥`. Since for any `a`, `const ⊥ a = ⊥`, it's strict in its 1st argument. In practice, this is because whenever we require a value from `const a b` we require a value from `a`, so `a` is returned in weak head normal form, i.e. evaluating `const a b` to WHNF inevitably evaluates `a` to WHNF. _Whenever `const` is actually applied_, the result is evaluated to WHNF, even though lazy evaluation means that `const` might not be applied. – AndrewC Dec 30 '14 at 10:16
  • @AndrewC That kind of is what I do, actually. Except that I didn't want to introduce `⊥` but decided to instead use `product [1..]` all along the article. Thanks for the feedback. I'll see if I can make things clearer one of these days. – Alp Mestanogullari Dec 30 '14 at 10:25
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/67908/discussion-between-andrewc-and-alp-mestanogullari). – AndrewC Dec 30 '14 at 11:51
  • As @AndrewC says, a function is strict iff `f ⊥ = ⊥`. Note that this definition has interesting consequences, e.g., `foo x y = ⊥`, here `foo` is strict in both `x` and `y`, but doesn't need either of them. – augustss Dec 30 '14 at 16:02
  • @augustss Now that _is_ counterintuitive. – AndrewC Dec 30 '14 at 17:04
  • 1
    @AndrewC Now add the optimization that strict arguments can be evaluated before a function is called, and you get even more fun. So `foo x y = error "A"; bar = foo 1 (error "B")` can result in either an A or B error, depending on the optimizer. But this is OK. Exception semantics in Haskell is non-deterministic and can only be observed in the IO monad. – augustss Dec 30 '14 at 20:32
  • @augustss @AlpMestanogullari If you look at the edit of my post, assigning `bar (product [1..]) 1` to a variable does not hang, it only hangs when we ask for the variable at ghci. Is variable binding different in that it doesn't need it's RHS to be in WHNF? – user2666425 Dec 30 '14 at 22:10
  • @augustss I'm OK with exception non-determinism, mainly since it's all ⊥ theoretically anyway. – AndrewC Dec 30 '14 at 22:17
  • @user2666425 At the risk of making further mistakes, I'll fail to be quiet and say that as always, the RHS of a function only ever needs to be evaluated when the LHS is evaluated. A variable binding introduces no such need, whereas a function that needs that argument can. – AndrewC Dec 30 '14 at 22:53

5 Answers5

12

First of all, Haskell does not specify when evaluation happens so the question can only be given a definite answer for specific implementations.

The following is true for all non-parallel implementations that I know of, like ghc, hbc, nhc, hugs, etc (all G-machine based, btw).

BTW, something to remember is that when you hear "evaluate" for Haskell it normally means "evaluate to WHNF".

Unlike strict languages you have to distinguish between two "callers" of a function, the first is where the call occurs lexically, and the second is where the value is demanded. For a strict language these two always coincide, but not for a lazy language. Let's take your example and complicate it a little:

foo [] = []
foo (_:xs) = map (* 2) xs

bar x = (foo [1..], x)

main = print (head (fst (bar 42)))

The foo function occurs in bar. Evaluating bar will return a pair, and the first component of the pair is a thunk corresponding to foo [1..]. So bar is what would be the caller in a strict language, but in the case of a lazy language it doesn't call foo at all, instead it just builds the closure.

Now, in the main function we actually need the value of head (fst (bar 42)) since we have to print it. So the head function will actually be called. The head function is defined by pattern matching, so it needs the value of the argument. So fst is called. It too is defined by pattern matching and needs its argument so bar is called, and bar will return a pair, and fst will evaluate and return its first component. And now finally foo is "called"; and by called I mean that the thunk is evaluated (entered as it's sometimes called in TIM terminology), because the value is needed. The only reason the actual code for foo is called is that we want a value. So foo had better return a value (i.e., a WHNF). The foo function will evaluate its argument and end up in the second branch. Here it will tail call into the code for map. The map function is defined by pattern match and it will evaluate its argument, which is a cons. So map will return the following {(*2) y} : {map (*2) ys}, where I have used {} to indicate a closure being built. So as you can see map just returns a cons cell with the head being a closure and the tail being a closure.

To understand the operational semantics of Haskell better I suggest you look at some paper describing how to translate Haskell to some abstract machine, like the G-machine.

augustss
  • 22,884
  • 5
  • 56
  • 93
  • Thank you for the reply. So, is `foo x y = x` strict in `x`, or strict in none of its arguments? And, you mention that when we finally enter `foo`, it too enters the body of `map`- but this returns `{(*2) y} : {map (*2) ys}`. If we enter the body of `map` from `foo,` why do we not also enter `map` again in the tail of `{(*2) y} : {map (*2) ys}`? Why do we return a thunk with a `map` call there, instead of just returning a thunk one step earlier in `foo` - is it because it *does* evaluate its return type, and puts it in WHNF? – user2666425 Dec 29 '14 at 21:10
  • The function `foo x y = x` is strict in `x`. All functions evaluate its result. So calling `foo` has to return a WHNF. Calling `map` will accomplish this. Another way would be to construct the thunk for calling `map` and then evaluate that at once. Calling `map` directly is an optimization of that. – augustss Dec 30 '14 at 05:48
  • A counterpoint presented in the comments of my question was that `length [const (error "oops") 1]` returns `1`- not an error, arguing that `const` therefore does not evaluate its result when it returns. My guess is that this is wrong- `const` does evaluate its result, but we just never actually enter `const`, because `length` doesn't need to see the contents of the thunk when it computes the length, is that correct? – user2666425 Dec 30 '14 at 06:08
  • Thank you very much for the comments and the help. I just want to be sure I understand- in your post, evaluating `bar` does not enter into `foo` *because* the result is already in WHNF- but when we are in `foo`, we *do* enter into `map` because we are *not* in WHNF yet. Right? In conclusion- functions in Haskell do (in general, GHC) evaluate their return value (to WHNF) and they are strict in arguments on which the return value depends. – user2666425 Dec 30 '14 at 06:35
  • 1
    @user2666425 Yes. `bar`'s result is in WHNF (the constructor is `(,)`, but both components of the tuple are left unevaluated _unless/until the calling code enters them_. For `foo`, to know what to do you have to figure out which pattern you'll match, that forces you to evaluate the list to WHNF (is it empty or is it a cons?), it's a cons -> second clause. And indeed, since `map (*2) xs` is not WHNF, we have to get far enough to be able to return either `[]` or a cons. So we need to evaluate map enough to know which list constructor is handed back. – Alp Mestanogullari Dec 30 '14 at 09:14
3

I always found that the term "evaluate," which I had learned in other contexts (e.g., Scheme programming), always got me all confused when I tried to apply it to Haskell, and that I made a breakthrough when I started to think of Haskell in terms of forcing expressions instead of "evaluating" them. Some key differences:

  • "Evaluation," as I learned the term before, strongly connotes mapping expressions to values that are themselves not expressions. (One common technical term here is "denotations.")
  • In Haskell, the process of forcing is IMHO most easily understood as expression rewriting. You start with an expression, and you repeatedly rewrite it according to certain rules until you get an equivalent expression that satisfies a certain property.

In Haskell the "certain property" has the unfriendly name weak head normal form ("WHNF"), which really just means that the expression is either a nullary data constructor or an application of a data constructor.

Let's translate that to a very rough set of informal rules. To force an expression expr:

  • If expr is a nullary constructor or a constructor application, the result of forcing it is expr itself. (It's already in WHNF.)
  • If expr is a function application f arg, then the result of forcing it is obtained this way:
    1. Find the definition of f.
    2. Can you pattern match this definition against the expression arg? If not, then force arg and try again with the result of that.
    3. Substitute the pattern match variables in the body of f with the parts of (the possibly rewritten) arg that correspond to them, and force the resulting expression.

One way of thinking of this is that when you force an expression, you're trying to rewrite it minimally to reduce it to an equivalent expression in WHNF.

Let's apply this to your example:

foo :: Num a => [a] -> [a]
foo [] = []
foo (_:xs) = map (* 2) xs

-- We want to force this expression:
head (foo [1..])

We will need definitions for head and `map:

head [] = undefined
head (x:_) = x

map _ [] = []
map f (x:xs) = f x : map f x

-- Not real code, but a rule we'll be using for forcing infinite ranges.
[n..] ==> n : [(n+1)..]

So now:

head (foo [1..]) ==> head (map (*2) [1..])       -- using the definition of foo
                 ==> head (map (*2) (1 : [2..])) -- using the forcing rule for [n..]
                 ==> head (1*2 : map (*2) [2..]) -- using the definition of map
                 ==> 1*2                         -- using the definition of head
                 ==> 2                           -- using the definition of *
Luis Casillas
  • 29,802
  • 7
  • 49
  • 102
2

I believe the idea must be that in a lazy language if you're evaluating a function application, it must be because you need the result of the application for something. So whatever reason caused the function application to be reduced in the first place is going to continue to need to reduce the returned result. If we didn't need the function's result we wouldn't be evaluating the call in the first place, the whole application would be left as a thunk.

A key point is that the standard "lazy evaluation" order is demand-driven. You only evaluate what you need. Evaluating more risks violating the language spec's definition of "non-strict semantics" and looping or failing for some programs that should be able to terminate; lazy evaluation has the interesting property that if any evaluation order can cause a particular program to terminate, so can lazy evaluation.1

But if we only evaluate what we need, what does "need" mean? Generally it means either

  1. a pattern match needs to know what constructor a particular value is (e.g. I can't know what branch to take in your definition of foo without knowing whether the argument is [] or _:xs)
  2. a primitive operation needs to know the entire value (e.g. the arithmetic circuits in the CPU can't add or compare thunks; I need to fully evaluate two Int values to call such operations)
  3. the outer driver that executes the main IO action needs to know what the next thing to execute is

So say we've got this program:

foo :: Num a => [a] -> [a]
foo [] = []
foo (_:xs) = map (* 2) xs

main :: IO ()
main = print (head (foo [1..]))

To execute main, the IO driver has to evaluate the thunk print (head (foo [1..])) to work out that it's print applied to the thunk head (foo [1..]). print needs to evaluate its argument on order to print it, so now we need to evaluate that thunk.

head starts by pattern matching its argument, so now we need to evaluate foo [1..], but only to WHNF - just enough to tell whether the outermost list constructor is [] or :.

foo starts by pattern matching on its argument. So we need to evaluate [1..], also only to WHNF. That's basically 1 : [2..], which is enough to see which branch to take in foo.2

The : case of foo (with xs bound to the thunk [2..]) evaluates to the thunk map (*2) [2..].

So foo is evaluated, and didn't evaluate its body. However, we only did that because head was pattern matching to see if we had [] or x : _. We still don't know that, so we must immediately continue to evaluate the result of foo.

This is what the article means when it says functions are strict in their result. Given that a call to foo is evaluated at all, its result will also be evaluated (and so, anything needed to evaluate the result will also be evaluated).

But how far it needs to be evaluated depends on the calling context. head is only pattern matching on the result of foo, so it only needs a result to WHNF. We can get an infinite list to WHNF (we already did so, with 1 : [2..]), so we don't necessarily get in an infinite loop when evaluating a call to foo. But if head were some sort of primitive operation implemented outside of Haskell that needed to be passed a completely evaluated list, then we'd be evaluating foo [1..] completely, and thus would never finish in order to come back to head.

So, just to complete my example, we're evaluating map (2 *) [2..].

map pattern matches its second argument, so we need to evaluate [2..] as far as 2 : [3..]. That's enough for map to return the thunk (2 *) 2 : map (2 *) [3..], which is in WHNF. And so it's done, we can finally return to head.

head ((2 *) 2 : map (2 *) [3..]) doesn't need to inspect either side of the :, it just needs to know that there is one so it can return the left side. So it just returns the unevaluated thunk (2 *) 2.

Again though, we only evaluated the call to head this far because print needed to know what its result is, so although head doesn't evaluate its result, its result is always evaluated whenever the call to head is.

(2 *) 2 evaluates to 4, print converts that into the string "4" (via show), and the line gets printed to the output. That was the entire main IO action, so the program is done.


1 Implementations of Haskell, such as GHC, do not always use "standard lazy evaluation", and the language spec does not require it. If the compiler can prove that something will always be needed, or cannot loop/error, then it's safe to evaluate it even when lazy evaluation wouldn't (yet) do so. This can often be faster so GHC optimizations do actually do this.

2 I'm skipping over a few details here, like that print does have some non-primitive implementation we could step inside and lazily evaluate, and that [1..] could be further expanded to the functions that actually implement that syntax.

Ben
  • 68,572
  • 20
  • 126
  • 174
  • In your explanation, why does `foo` return a thunk of `map (* 2) [2..]` instead of evaluating it to WHNF itself? – user2666425 Dec 30 '14 at 02:18
  • @user2666425 Why would it? It doesn't pattern match on its own result, so it has no need to know what constructor it returns. Of course, it could *as an optimization*, since whenever it is called to return anything its result is about to be evaluated anyway. – Ben Dec 30 '14 at 02:35
  • So in general there is no promise that a function returns its result in WHNF? Actually, if you look at the comments on my question, someone says 'That's certainly the answer for ghc. The foo function does NOT return a thunk'. So confused. – user2666425 Dec 30 '14 at 02:38
  • 2
    @user2666425 My answer is speaking more to theory than the practical implementation. The theory much simpler than GHC actually is, and most of the time is all you need to understand to predict how things work. The point is that the "need" to evaluate foo's result comes from the caller of foo, not from foo itself. But since such a need always exists, GHC can implement foo such that it returns an evaluated result. – Ben Dec 30 '14 at 03:26
0

Not necessarily. Haskell is lazy, meaning that it only evaluates when it needs to. This has some interesting effects. If we take the below code, for example:

-- File: lazinessTest.hs
(>?) :: a -> b -> b
a >? b = b

main = (putStrLn "Something") >? (putStrLn "Something else")

This is the output of the program:

$ ./lazinessTest
Something else

This indicates that putStrLn "Something" is never evaluated. But it's still being passed to the function, in the form of a 'thunk'. These 'thunks' are unevaluated values that, rather than being concrete values, are like a breadcrumb-trail of how to compute the value. This is how Haskell laziness works.

In our case, two 'thunks' are passed to >?, but only one is passed out, meaning that only one is evaluated in the end. This also applies in const, where the second argument can be safely ignored, and therefore is never computed. As for map, GHC is smart enough to realise that we don't care about the end of the array, and only bothers to compute what it needs to, in your case the second element of the original list.

However, it's best to leave the thinking about laziness to the compiler and keep coding, unless you're dealing with IO, in which case you really, really should think about laziness, because you can easily go wrong, as I've just demonstrated.

There are lots and lots of online articles on the Haskell wiki to look at, if you want more detail.

AJF
  • 11,767
  • 2
  • 37
  • 64
  • Looking at the output is not a good indicator here. Even if the first call to putStrLn was evaluated, that would not cause any output. – kosmikus Dec 29 '14 at 17:16
  • Expanding on kosmikus' comment, there's a difference between a thunk being evaluated and an IO action being executed. All you're shown is that the first putStrLn isn't executed (and since you wrote a program that simply doesn't execute it, that's not very interesting); but as far as this experiment shows, it may well have been evaluated before it was discarded without being executed. – Ben Dec 30 '14 at 00:25
-2

Function could evaluate either return type:

head (x:_) = x

or exception/error:

head _ = error "Head: List is empty!"

or bottom (⊥)

a = a
b = last [1 ..]
viorior
  • 1,783
  • 11
  • 16