Let's take the naive implementation of fix
:
fix f = f (fix f)
For a function f
, this creates something like the following:
f (f (f (f (f (f (f ...
The Fix
newtype does the same, but for types. So if we take the type Maybe
, we would want to create:
Maybe (Maybe (Maybe (Maybe (Maybe (Maybe ...
How would we create a function which constructs that type? We can first try with a type synonym:
-- fix f = f (fix f)
type Fix f = f (Fix f)
You should be able to see that this is the same as the naive implementation of fix
above with some minor changes. But it's not legal Haskell!
This is for a number of reasons: mainly, Haskell doesn't allow infinite types like the Maybe
example above, and Haskell's type system is strict, in contrast to the lazy evaluation required in fix
. That's why we need a newtype
. New type definitions (introduced with newtype
or data
) allow recursion, so we take our type synonym and change it into a newtype.
type Fix f = f (Fix f)
newtype Fix f = f (Fix f) -- change to newtype
newtype Fix f = Fix (f (Fix f)) -- need to have a constructor
newtype Fix f = Fix { unFix :: f (Fix f) } -- And name the field, also