| Copyright | (C) 2017 Ryan Scott |
|---|---|
| License | BSD-style (see the file LICENSE) |
| Maintainer | Ryan Scott |
| Stability | Experimental |
| Portability | GHC |
| Safe Haskell | Unsafe |
| Language | Haskell2010 |
Data.Eliminator.TH
Contents
Description
Generate dependently typed elimination functions using Template Haskell.
- deriveElim :: Name -> Q [Dec]
- deriveElimNamed :: String -> Name -> Q [Dec]
Eliminator generation
deriveElim and deriveElimNamed provide a way to automate the creation of
eliminator functions, which are mostly boilerplate. Here is a complete example
showing how one might use deriveElim:
$(singletons[d| data MyList a = MyNil | MyCons a (MyList a) |]) $(deriveElim''MyList)
This will produce an eliminator function that looks roughly like the following:
elimMyList :: forall (a ::Type) (p :: MyList a~>Type) (l :: MyList a).Singl -> p@@MyNil -> (forall (x :: a).Singx -> forall (xs :: MyList a).Singxs -> p@@xs -> p@@(MyCons x xs)) -> p@@l elimMyList SMyNil pMyNil _ = pMyNil elimMyList (SMyCons (x' ::Singx) (xs' ::Singxs)) pMyNil pMyCons = pMyCons x' xs' (elimMyList @a @p @xs pMyNil pMyCons)
There are some important things to note here:
- Because these eliminators use
Singunder the hood, in order forderiveElimto work, theSinginstance for the data type given as an argument must be in scope. Moreover,deriveElimassumes the naming conventions for singled constructors used by thesingletonslibrary. (This is why thesingletonsfunction is used in the example above). There is a convention for the order in which the arguments appear. The quantified type variables appear in this order:
The function arguments appear in this order:
- First, a
Singargument (, in the above example).Singl - Next, there are arguments that correspond to each constructor. More on this in a second.
The return type is the predicate type variable applied to the data type
(p '' (MyCons x xs), the above example).
The type of each constructor argument also follows certain conventions:
- For each field, there will be a rank-2 type variable whose kind matches
the type of the field, followed by a matching
Singtype. For instance, in the above example,forall (x :: a).corresponds to the first field ofSingxMyCons. - In addition, if the field is a recursive occurrence of the data type,
an additional argument will follow the
Singtype. This is best explained using the above example. In theMyConsconstructor, the second field (of typeMyCons a) is a recursive occurrence ofMyCons, so that corresponds to the typeforall (xs :: MyList a).Singxs -> p '' xs, wherep '' xsis only present due to the recursion. - Finally, the return type will be the predicate type variable applied
to a saturated occurrence of the data constructor
(
p '' (MyCons x xs), in the above example).
You'll need to enable lots of GHC extensions in order for the code generated by
deriveElimto typecheck. You'll need at least the following:AllowAmbiguousTypes
GADTs
RankNTypes
ScopedTypeVariables
TemplateHaskell
TypeApplications
TypeInType
deriveElimdoesn't support every possible data type at the moment. It is known not to work for the following:- Data types defined using
GADTsorExistentialQuantification - Data family instances
- Data types which use polymorphic recursion
(e.g.,
data Foo a = Foo (Foo a))
- Data types defined using
deriveElim :: Name -> Q [Dec] Source #
generates a top-level elimination function for the
datatype deriveElim dataNamedataName. The eliminator will follow these naming conventions:
The naming conventions are:
- If the datatype has an alphanumeric name, its eliminator will have that name
with
elimprepended. - If the datatype has a symbolic name, its eliminator will have that name
with
~>prepended.
deriveElimNamed :: String -> Name -> Q [Dec] Source #
generates a top-level elimination
function named deriveElimNamed funName dataNamefunName for the datatype dataName.