packages feed

yoko-0.1: README

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

...