# [Haskell-cafe] strict, lazy, non-strict, eager

wren ng thornton wren at freegeek.org
Mon Jan 9 10:43:34 CET 2012

```On 12/28/11 6:47 AM, Thiago Negri wrote:
> I got a glimpse of understanding of what you are talking about after
>
> Still difficult to reason about the difference between lazy and
> non-strict without taking a look at the text.

One way to keep them distinct is to recall that "lazy" is quite a bit
more specific than "non-strict". Let's take the wiki's definition:

non-strict means evaluation from outside in

(however problematic that may be). Now, just because we've specified
this directionality of evaluation, that doesn't mean we've said
everything we need to say about evaluation order. In particular, there
are two common/obvious implementations of non-strict semantics:
call-by-name, and call-by-need.

In call-by-name evaluation, we just pass in the argument to functions as
a literal expression. This is easy to reason about mathematically,
however it has poor performance because it can cause duplication of
effort. Namely, if a function uses its argument N times, then
beta-reduction will create N copies of the argument, each of which will
be evaluated (or not) separately.

In call-by-need (aka "lazy") evaluation, we don't just pass the
expression in duplicating it if need be. Instead, we create a thunk and
replace all references to the argument with references/pointers to the
thunk. In this way, we can share the evaluation effort since we only
have to evaluate/mutate the thunk once and the result can be shared
across all use sites.

In other words, in call-by-name beta-reduction is the familiar:

(\x. a) \$ b
-----------
[x |-> b] a

where [_|->_]_ is literal substitution of expressions for variables in
expressions. Whereas in call-by-need, beta-reduction is something like:

(\x. a) \$ b
-----------
let x = b in a

where let_=_in_ is a primitive construct for expressing shared
substructure. Notably, in call-by-need the locus of evaluation shifts to
a, so we evaluate under let_=_in_. The locus of evaluation only ever
moves to b if indeed x is forced within a.

In practice, GHC (and other Haskell compilers) are not lazy. That is,
they do not exclusively use the call-by-need evaluation order. Instead,
there is a hybridization of call-by-name, call-by-need, and
call-by-value, the choice of which being based on the strictness
analyzer and other optimization passes. The fact that GHC is allowed to
do this can still call itself "Haskell" stems from the fact that the
semantics of Haskell are defined merely as being non-strict, rather than
being more specific.

--
Live well,
~wren

```