kind-generics-th
Template Haskell support for generating `GenericK` instances
| LTS Haskell 22.44: | 0.2.3.3 |
| Stackage Nightly 2025-10-24: | 0.2.3.3@rev:1 |
| Latest on Hackage: | 0.2.3.3@rev:1 |
kind-generics-th-0.2.3.3@sha256:c53280116db0dab9891f5a531679073f0b1f9ba6b8a3399bca11d98c506d0c62,1578Module documentation for 0.2.3.3
- Generics
- Generics.Kind
kind-generics-th: Template Haskell support for generating GenericK instances
This package provides Template Haskell functionality to automatically derive
GenericK instances. Currently, this only supports the version of GenericK
as found in the kind-generics library. (The GenericK class found in
kind-generics-sop is not supported at the moment.)
How to use this library
To derive instances of GenericK for a data type, simply pass the Template
Haskell–quoted Name of the type to the deriveGenericK function, as in the
following example:
$(deriveGenericK ''Either)
If you wish to pass a data family instance, one can pass the name of a constructor belonging to the particular family instance, such as in the following example:
data family Foo a b
data instance Foo Int b = MkFooInt b
$(deriveGenericK 'MkFooInt)
You will likely need to enable most of these language extensions in order for GHC to accept the generated code:
DataKindsEmptyCase(if using an empty data type)FlexibleInstancesMultiParamTypeClassesPolyKinds(if using a poly-kinded data type)TemplateHaskellTypeFamiliesUndecidableInstances(if using a data type involving type families)
Type families
If the data type uses a type family (more precisely, if a type
variable occurs in a type family application), deriveGenericK
will warn that it won’t generate all of the instances that you’d
normally expect, and it will tell you what to do if you do want those
instances or if you want to silence the warning.
type family F a
data T a = C (F a)
$(deriveGenericK ''T)
Warning message:
Found type family in definition of ''T. Some instances have been skipped.
Declared instances:
instance GenericK (T a)
Skipped instances:
instance GenericK T
To enable type family support and obtain those skipped instances:
$(preDeriveGenericK ''T)
$(postDeriveGenericK ''T)
To silence this warning:
$(deriveGenericKQuiet ''T)
How many GenericK instances are generated
deriveGenericK typically generates multiple GenericK instances per data
type, as there is one GenericK instance per partial application of a data
type constructor. For instance, $(deriveGenericK ''Either) will generate
three GenericK instances:
instance GenericK (Either a b) where ...
instance GenericK (Either a) where ...
instance GenericK Either where ...
Not every data type can be partially applied all the way in this fashion, however. Some notable counterexamples are:
-
Data family instances. In the following example:
data family Bar a b data instance Bar a a = MkBar aOne cannot partially apply to
Bar a ato simplyBar a, so$(deriveGenericK 'MkBar)will only generate a single instance forGenericK (Bar a a). -
Dependent kinds.
kind-genericsis not currently capable of representing data types such as the following in their full generality:data Baz k (a :: k)Because the
ktype variable is used in the kind ofa(i.e., it is used in a visible, dependent fashion). As a consequence,$(deriveGenericK ''Baz)will only generate the following instances:instance GenericK (Baz k a)instance GenericK (Baz k)
Limitations
kind-generics is capable of representing a wide variety of data types. The
Template Haskell machinery in this library makes a best-effort attempt to
automate the creation of most of these instances, but there are a handful of
corner cases that it does not handle well. This section documents all of the
known limitations of deriveGenericK:
-
Data constructors with rank-n field types (e.g.,
(forall a. a -> a)) are partially supported:foralland constraintsc =>are allowed only at the root of a field’s type.data Ok = Ok { a :: forall a. a -> a , b :: forall a b. Eq a => a -> b } data NotOk = NotOk { c :: (forall a. a -> a) -> Bool } -
Data constructors with unlifted field types (e.g.,
Int#or(# Bool #)) are unlikely to work. -
GADTs that make use of certain forms of kind equalities are currently not supported. For example:
data Quux (a :: k) where MkQuux :: forall (a :: *). Maybe a -> Quux aIf one were to rewrite
Quuxto make the existential quantification explicit, it would look like this:data Quux (a :: k) = forall (a' :: *). (k ~ Type, a' ~~ a) => MkQuux (Maybe a')Therefore, we ought to get a
GenericKinstance like this:instance GenericK (Quux :: k -> *) where type RepK (Quux :: k -> *) = Exists * ((Kon (k ~ Type) :&: (Var0 :~~: Var1)) :=>: Field (Maybe :$: Var0)) ...Devising an algorithm that converts the original GADT definition of
Quuxinto the explicitly existential form is not straightforward, however. In particular,deriveGenericKonly detects thek ~ *part correctly at the moment, so it will generate an ill kinded instance forQuux. -
While there is support for data types that use type families in their fields, they cannot be dependently typed, i.e., the result type may not depend on visible arguments.