drex (read "dee-recks")
While the "d" is just for "datatype", "rex" is bit of a double entendre.
The representation classes (DT, Generic, Gist) each disinvest their parameter
of its nominality, similar to how a king disinvests nobles of their titles.
Each class also dismantles, or "wrecks", its parameter: DT takes a single type
to a sum of its constructors, Generic maps a constructor to its underlying
shape, and Gist forgets the shape and mediation.
... Also, it sounds like "T-rex".
===================
See http://j.mp/tNLx40 for a Google spreadsheet cataloging the various d-rex
concepts and components.
====================
#1 Basic Universes
The @:::@ class in the @Universe@ module is pervasive in d-rex. The constraint
@t ::: u@ is read "t inhabits u" (or "t satisfies u", if you must). @u@ is a
/universe/, a type that represents a possibly finite, possibly paradoxical
collection of types. Universes can be /open/ or /closed/. @Lit@, for example,
is closed.
in module Ex
data Lit t where IntLit :: Lit Int; CharLit :: Lit Char
@ShowD@ is open, since new instances of @Show@ can be declared anywhere.
in module Ex
data ShowD t where ShowD :: Show t => ShowD t
(The "D" suffix is for "dictionary", since this GADT operationally reifies the
@Show@ dictionary. @(\ShowD x -> show x) :: ShowD t -> t -> String@ -- note
that there's no @Show t@ constraint in that type.
Some closed universes are also finite. There exists an isomorphism between such
a universes and a finite set of types (#4 below).
#2 Constructor Universes
d-rex's principle novelty is its support of the finite closed universe of a
datatype's constructors, codifed as the indexed data family @DCU@. The @open@
method of the @DT@ type class converts from a type to its universe of
constructors. @close@ goes back the other way.
As d-rex breaks a datatype into its universe of constructors, it also generates
a new void type per constructor. For example, d-rex breaks @Either a b@ into
in module G
data Left a b; data Right a b
With these types, d-rex declares the constructor universe of @Either a b@.
in module G
data instance DCU (Either a b) where
Left_ :: DCU (Either a b) (Left a b)
Right_ :: DCU (Either a b) (Right a b)
Note that each constructor type inherits the original type's parameters. d-rex
also declares an instance of the data family @RM@ for each constructor -- the
resulting types are called /fields types/.
in module G
newtype instance RM (N (Left a b)) m = Left a
newtype instance RM (N (Right a b)) m = Right b
in module ReflectBaseR
type Fields dc = RM (N dc)
(Clearly, d-rex re-uses the constructor names. Hence, the generic declarations
must always generated in a separate module to enable namespace management.)
The data family @RM@, the @m@ parameter, type family @App@, and @N@ are
explained in the next section. In the interim, we'll make do with a couple
brief declarations.
in module Type
data IdT; type instance App IdT a = a
There now exists an isomorphism between @Either a b@ and
@forall dc. (DCU (Either a b) dc, Fields dc IdT).
Left x =~= (Left_, G.Left x)
Right x =~= (Right_, G.Right x)
The @DCU@ tag is a crucial part of this pair -- without it, G.Left and G.Right
would have inequal types!
#3 Recursion-mediated types
The data family @RM@ stands for "recursion-mediated". The idea is that the @m@
parameter is applied to all recursive type occurences in a constructor's
fields.
in module T
data Even a = Zero | Even a (Odd a)
data Odd a = Odd a (Even a)
in module G
data Zero a; data Even a; data Odd a
data instance RM (N (Zero a)) m
data instance RM (N (G.Even a)) m = Even a (App m (T.Odd a))
data instance RM (N (G.Odd a)) m = Odd a (App m (T.Even a))
in module Type
data True = True; data False = False
in module Ex
data ParityM
type instance App ParityM (T.Even a) = False
type instance App ParityM (T.Odd a) = True
ex0 = Even 'e' True :: Fields (G.Even Char) ParityM
ex1 = Odd 'o' False :: Fields (G.Odd Char) ParityM
The recursion-mediated representation of the fields types enables their
re-use. For example, the same fields type can be used to define a bottom-up
reducer, where the recursive occurrences have been replaced with the result of
the catamorphism.
in module Ex
data LengthM
type instance App LengthM (T.Even a) = Int
type instance App LengthM (T.Odd a) = Int
type Reducer m dc = Fields dc m -> App m dc
ex2 :: Reducer Len (G.Even a)
ex2 (G.Even _ i) = 1 + i
Since @App@ is a type family, it's not necessarily injective. @LengthM@
demonstrates where injectivity would not be desirable. Indeed, d-rex relies on
this as discussed in #6 below. Unfortunately, non-injectivity can muddle type
inference. For example, the inferred type of @G.Even 'c' 3@ involves a type
variable: @(Num i, App m (T.Even Char) ~ i) => Fields (G.Even Char) m@. We
provide the function @mediated@ for directly specifying the mediator.
in module Util
mediated :: [qP|m|] -> RM c m -> RM c m
mediated = const id
in module Ex
-- inferred ex3 :: Fields (G.Even Char) LengthM
ex3 = mediated [qP|LengthM|] $ G.Even 'c' 3
(@qP@ is just a quasiquoter for proxies -- useful for passing types as values.)
The data family @RM@ is indexed by the core representational types. Most of
these are common to many representation-based generic programming
libraries. They indeed compromise a closed universe @Core@; that particular
universe per se is not codified in d-rex, but its closedness is the crux of all
representational generic programming.
in module Core
type family Rep a
data V -- void
data U = U -- unit
newtype D a = D a -- a dependency
newtype R t = R t -- a recursive occurrence
newtype F f c = F (f c) -- argument to a *->*
newtype FF ff c d = FF (ff c d) -- arguments to a *->*->*
newtype M i c = M c -- meta information
newtype N t = N t -- a named type (user hook)
type (:+) = FF Either
type (:*) = FF (,)
type (:->) = FF (->)
The structure of many constructors' fields, like T.Even can be codified in
terms of these basic types.
in module G
type Rep (G.Even a) = FF (,) (D a) (R a)
The recursion-mediated types are indexed by these core types. Note that the
following declarations are in a separate module, so the constructor names don't
actually clash.
in module GenericR
data family RM c m
data instance RM V m
data instance RM U m = U
newtype instance RM (D a) m = D a
newtype instance RM (R t) m = R (App m t)
newtype instance RM (F f c) m = F (f (RM c m))
newtype instance RM (FF ff c d) m = FF (ff (RM c m) (RM d m))
newtype instance RM (M i c) m = M (RM c m)
These just follow the semantics of recusiion-mediated types.
The only core type without an @RM@ instance is @N@. @N@ is crucial to d-rex's
usability. It is the interface boundary between the d-rex kernel and the user
datatype. As demonstrated earlier in this section and in section #2, @RM (N -)@
instances are provided for each fields type. There is also a corresponding
instance of @Rep@ and @Generic@.
in module GenericR
class Generic a where
rep :: RM (N a) m -> RM (Rep a) m
obj :: RM (Rep a) m -> RM (N a) m
in module G
instance Generic (G.Even a) where
rep ~(G.Even a o) = FF (D a, R o)
obj ~(FF (D a, R o))) = G.Even a o
INVARIANT: the RHS of a Rep should never include an @N@. @N@ is just in place
to delay the representation of a type.
#4 Sets of types
d-rex also uses a universe of types constructed via V, :+, and N to represent
finite sets (implemented as type-level binary trees) of types. The @Finite@
type class recognizes the isomorphism between a finite closed universes and a
set of types.
in module TypeBTree
type family Inhabitants u
class Finite u where
path :: u t -> Small (Inhabitants u) t
tag :: Small (Inhabitants u) t -> u t
in module Ex
type instance Inhabitants (DCU Lit) = N Int :+ N Char
type instance Inhabitants (DCU (Either a b)) =
N (G.Left a b) :+ N (G.Right a b)
type instance Inhabitants (DCU (T.Even a)) =
N (G.Zero a) :+ N (G.Even a)
@Inhabitants@/@Finite@ recognizes @V@, @:+@, and @N@ as the closed
representational core of finite closed universes in the same way that
@Rep@/@Generic@ encode isomorphisms between the full ensemble of core types and
the large set of Haskell types they can represent.
#5 Other Universes
Exists, Small, All, MFun, MMap ...
#6 Tag-Gist equivalence and Conversions
...