Formally this is known as η-conversion for the unit type. It would be troublesome in the presence of seq
. For example, deepseq
defines
class NFData a where
rnf :: a -> ()
where evaluating rnf blah
should ensure blah
is fully evaluated to normal form, then return ()
. That’s sometimes useful to avoid space leaks. But it would become useless if rnf blah
was silently η-converted to ()
.
Because as far as I understand, the compiler has to store a boxed representation for ()
, just to account for _|_
. If this optimization was legal, ()
could be stored in zero space, allowing considerable optimization and code reuse (Set e
could be implemented as Map e ()
, for example, with no loss of efficiency).
With -XUnboxedTuples
, GHC does actually have an unboxed unit type, namely (# #)
. However its kind is not Type
, so you can’t store it in a Map
. This is an instance of the general property that unboxed things can’t be stored in polymorphic datatypes (because the polymorphic code has to know the runtime representation of the data, i.e. it has to be boxed). One could imagine a compiler that did things differently, but it would be very different to GHC.
You might find the GHC user’s guide on this topic helpful: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/exts/primitives.html