This is the usual definition of the fixed-point combinator in Haskell:
fix :: (a -> a) -> a
fix f = let x = f x in x
On https://wiki.
_Y
is translated to the following STG:
_Y f = let x = _Y f in f x
fix
is translated identically to the Haskell source:
fix f = let x = f x in x
So fix f
sets up a recursive thunk x
and returns it, while _Y
is a recursive function, and importantly it’s not tail-recursive. Forcing _Y f
enters f
, passing a new call to _Y f
as an argument, so each recursive call sets up a new thunk; forcing the x
returned by fix f
enters f
, passing x
itself as an argument, so each recursive call is into the same thunk—this is what’s meant by “sharing”.
The sharing version usually has better memory usage, and also lets the GHC RTS detect some kinds of infinite loop. When a thunk is forced, before evaluation starts, it’s replaced with a “black hole”; if at any point during evaluation of a thunk a black hole is reached from the same thread, then we know we have an infinite loop and can throw an exception (which you may have seen displayed as Exception: <
).