How does 'lazy' work?
- by Matt Fenwick
What is the difference between these two functions? I see that lazy is intended to be lazy, but I don't understand how that is accomplished.
-- | Identity function.
id :: a -> a
id x = x
-- | The call '(lazy e)' means the same as 'e', but 'lazy' has a
-- magical strictness property: it is lazy in its first argument,
-- even though its semantics is strict.
lazy :: a -> a
lazy x = x
-- Implementation note: its strictness and unfolding are over-ridden
-- by the definition in MkId.lhs; in both cases to nothing at all.
-- That way, 'lazy' does not get inlined, and the strictness analyser
-- sees it as lazy. Then the worker/wrapper phase inlines it.
-- Result: happiness
Tracking down the note in MkId.lhs (hopefully this is the right note and version, sorry if it's not):
Note [lazyId magic] ~~~~~~~~~~~~~~~~~~~
lazy :: forall a?. a? -> a? (i.e. works for unboxed types too)
Used to lazify pseq: pseq a b = a `seq` lazy b
Also, no strictness: by being a built-in Id, all the info about lazyId
comes from here, not from GHC.Base.hi. This is important, because
the strictness analyser will spot it as strict!
Also no unfolding in lazyId: it gets "inlined" by a HACK in CorePrep.
It's very important to do this inlining after unfoldings are exposed
in the interface file. Otherwise, the unfolding for (say) pseq in the
interface file will not mention 'lazy', so if we inline 'pseq' we'll
totally miss the very thing that 'lazy' was there for in the first
place. See Trac #3259 for a real world example.
lazyId is defined in GHC.Base, so we don't have to inline it. If it
appears un-applied, we'll end up just calling it.
I don't understand that because it refers to lazyId instead of lazy. How does lazy work?