6.4.13. Representation polymorphism¶
In order to allow full flexibility in how kinds are used, it is necessary
to use the kind system to differentiate between boxed, lifted types
(normal, everyday types like
[Bool]) and unboxed, primitive
types (Unboxed types and primitive operations) like
Int#. We thus have so-called representation
Here are the key definitions, all available from
TYPE :: RuntimeRep -> Type -- highly magical, built into GHC data Levity = Lifted -- for things like `Int` | Unlifted -- for things like `Array#` data RuntimeRep = BoxedRep Levity -- for anything represented by a GC-managed pointer | IntRep -- for `Int#` | TupleRep [RuntimeRep] -- unboxed tuples, indexed by the representations of the elements | SumRep [RuntimeRep] -- unboxed sums, indexed by the representations of the disjuncts | ... type LiftedRep = BoxedRep Lifted type Type = TYPE LiftedRep -- Type is just an ordinary type synonym
The idea is that we have a new fundamental type constant
is parameterised by a
RuntimeRep. We thus get
Int# :: TYPE IntRep
Bool :: TYPE LiftedRep. Anything with a type of the form
TYPE x can appear to either side of a function arrow
->. We can
thus say that
-> has type
TYPE r1 -> TYPE r2 -> TYPE LiftedRep. The result is always lifted
because all functions are lifted in GHC.
188.8.131.52. Levity polymorphism¶
A special case of representation polymorphism is levity polymorphism,
where we abstract over a variable of kind
Levity, such as:
example :: forall (l :: Levity) (a :: TYPE (BoxedRep l)). (Int -> a) -> a example f = f 42
UnliftedDatatypes, we can even declare levity-polymorphic
type PEither :: Type -> Type -> TYPE (BoxedRep l) data PEither l r = PLeft l | PRight r
184.108.40.206. No representation-polymorphic variables or arguments¶
If GHC didn’t have to compile programs that run in the real world, that would be the end of the story. But representation polymorphism can cause quite a bit of trouble for GHC’s code generator. Consider
bad :: forall (r1 :: RuntimeRep) (r2 :: RuntimeRep) (a :: TYPE r1) (b :: TYPE r2). (a -> b) -> a -> b bad f x = f x
This seems like a generalisation of the standard
$ operator. If we
think about compiling this to runnable code, though, problems appear.
In particular, when we call
bad, we must somehow pass
bad. How wide (that is, how many bits) is
x? Is it a pointer?
What kind of register (floating-point or integral) should
x go in?
It’s all impossible to say, because
a :: TYPE r1 is
representation-polymorphic. We thus forbid such constructions, via the
following straightforward rule:
No variable may have a representation-polymorphic type.
bad because the variable
x would have a
However, not all is lost. We can still do this:
($) :: forall r (a :: Type) (b :: TYPE r). (a -> b) -> a -> b f $ x = f x
b is representation-polymorphic. There are no variables
with a representation-polymorphic type. And the code generator has no
trouble with this. Indeed, this is the true type of GHC’s
slightly more general than the Haskell 98 version.
Because the code generator must store and move arguments as well as variables, the logic above applies equally well to function arguments, which may not be representation-polymorphic.
220.127.116.11. Representation-polymorphic bottoms¶
We can use representation polymorphism to good effect with
undefined, whose types are given here:
undefined :: forall (r :: RuntimeRep) (a :: TYPE r). HasCallStack => a error :: forall (r :: RuntimeRep) (a :: TYPE r). HasCallStack => String -> a
These functions do not bind a representation-polymorphic variable, and so are accepted. Their polymorphism allows users to use these to conveniently stub out functions that return unboxed types.
18.104.22.168. Printing representation-polymorphic types¶
Levityparameters as they appear; otherwise, they are defaulted to
Most GHC users will not need to worry about representation polymorphism
or unboxed types. For these users, seeing the representation polymorphism
in the type of
$ is unhelpful. And thus, by default, it is suppressed,
by supposing all type variables of type
RuntimeRep to be
when printing, and printing
TYPE LiftedRep as
StarIsType is on).
Should you wish to see representation polymorphism in your types, enable
-fprint-explicit-runtime-reps. For example,
ghci> :t ($) ($) :: (a -> b) -> a -> b ghci> :set -fprint-explicit-runtime-reps ghci> :t ($) ($) :: forall (r :: GHC.Types.RuntimeRep) a (b :: TYPE r). (a -> b) -> a -> b