6.4.12. Type-level data declarations¶
-
TypeData
¶ Since: 9.6.1 Allow
type data
declarations, which define constructors at the type level.
This extension facilitates type-level (compile-time) programming by
allowing a type-level counterpart of data
declarations, such as this
definition of type-level natural numbers:
type data Nat = Zero | Succ Nat
This is similar to the corresponding data
declaration, except that
the constructors Zero
and Succ
it introduces belong to the type
constructor namespace, so they can be used in types, such as the type
of length-indexed vectors:
data Vec :: Type -> Nat -> Type where
Nil :: Vec a Zero
Cons :: a -> Vec a n -> Vec a (Succ n)
TypeData
is a more fine-grained alternative to the
DataKinds
extension, which defines all the constructors
in all data
declarations as both data constructors and type
constructors.
A type data
declaration has the same syntax as a data
declaration,
either an ordinary algebraic data type or a GADT, prefixed with the keyword
type
, except that it may not contain
a datatype context (even with DatatypeContexts
),
labelled fields,
strictness flags, or
a deriving
clause.
The only constraints permitted in the types of constructors are equality constraints, e.g.:
type data P :: Type -> Type -> Type where
MkP :: (a ~ Natural, b ~~ Char) => P a b
Because type data
declarations introduce type constructors, they do
not permit constructors with the same names as types, so the following
declaration is invalid:
type data T = T // Invalid
The compiler will reject this declaration, because the type constructor
T
is defined twice (as the datatype being defined and as a type
constructor).
The main type constructor of a type data
declaration can be defined
recursively, as in the Nat
example above, but its constructors may not
be used in types within the same mutually recursive group of declarations,
so the following is forbidden:
type data T f = K (f (K Int)) // Invalid