Copyright | (c) Atze van der Ploeg 2013 |
---|---|
License | BSD-style |
Maintainer | atzeus@gmail.org |
Stability | expirimental |
Safe Haskell | None |
Language | Haskell98 |
This module implements extensible records using closed type famillies.
See the examples.
Lists of (label,type) pairs are kept sorted thereby ensuring that { x = 0, y = 0 } and { y = 0, x = 0 } have the same type.
In this way we can implement standard type classes such as Show, Eq, Ord and Bounded for open records, given that all the elements of the open record satify the constraint.
- data Label s = Label
- class KnownSymbol n
- data Rec r
- data Row a
- empty :: Rec Empty
- type family Empty :: Row *
- extend :: KnownSymbol l => Label l -> a -> Rec r -> Rec (Extend l a r)
- extendUnique :: (KnownSymbol l, r :\ l) => Label l -> a -> Rec r -> Rec (Extend l a r)
- type family Extend l a r :: Row *
- rename :: (KnownSymbol l, KnownSymbol l') => Label l -> Label l' -> Rec r -> Rec (Rename l l' r)
- renameUnique :: (KnownSymbol l, KnownSymbol l', r :\ l') => Label l -> Label l' -> Rec r -> Rec (Rename l l' r)
- type family Rename l l' r :: Row *
- (.-) :: KnownSymbol l => Rec r -> Label l -> Rec (r :- l)
- type family r :- s :: Row *
- update :: KnownSymbol l => Label l -> (r :! l) -> Rec r -> Rec r
- (.!) :: KnownSymbol l => Rec r -> Label l -> r :! l
- type family r :! t :: *
- (.++) :: Rec l -> Rec r -> Rec (l :++ r)
- type family l :++ r :: Row *
- (.+) :: Disjoint l r => Rec l -> Rec r -> Rec (l :+ r)
- type family l :+ r :: Row *
- type (:\) r l = LacksP l r ~ LabelUnique l
- type Disjoint l r = DisjointR l r ~ IsDisjoint
- class Labels r where
- class Forall r c where
- data CWit c
- data FWit f
- data RecOp c rowOp where
- (:<-) :: KnownSymbol l => Label l -> a -> RecOp (HasType l a) RUp
- (:=) :: KnownSymbol l => Label l -> a -> RecOp NoConstr (l ::= a)
- (:!=) :: KnownSymbol l => Label l -> a -> RecOp (Lacks l) (l ::= a)
- (:<-|) :: (KnownSymbol l, KnownSymbol l') => Label l' -> Label l -> RecOp NoConstr (l' ::<-| l)
- (:<-!) :: (KnownSymbol l, KnownSymbol l', r :\ l') => Label l' -> Label l -> RecOp (Lacks l') (l' ::<-| l)
- data RowOp a where
- (.|) :: c r => RecOp c ro -> Rec r -> Rec (ro :| r)
- type family x :| r :: Row *
Types and constraints
class KnownSymbol n
This class gives the integer associated with a type-level symbol. There are instances of the class for every concrete literal: "hello", etc.
Since: 4.7.0.0
symbolSing
A record with row r.
The kind of rows. This type is only used as a datakind. A row is a typelevel entity telling us which symbols are associated with which types.
Construction
Extension
extend :: KnownSymbol l => Label l -> a -> Rec r -> Rec (Extend l a r) Source
Record extension. The row may already contain the label,
in which case the origin value can be obtained after restriction (.-
) with
the label.
extendUnique :: (KnownSymbol l, r :\ l) => Label l -> a -> Rec r -> Rec (Extend l a r) Source
Record extension without shadowing. The row may not already contain label l.
type family Extend l a r :: Row * Source
Type level operations of extend
and extendUnique
Extend l a (R x) = R (Inject (l :-> a) x) |
Renaming
rename :: (KnownSymbol l, KnownSymbol l') => Label l -> Label l' -> Rec r -> Rec (Rename l l' r) Source
Rename a label. The row may already contain the new label ,
in which case the origin value can be obtained after restriction (.-
) with
the new label.
renameUnique :: (KnownSymbol l, KnownSymbol l', r :\ l') => Label l -> Label l' -> Rec r -> Rec (Rename l l' r) Source
Rename a label. The row may not already contain the new label.
type family Rename l l' r :: Row * Source
Type level operation of rename
and renameUnique
Restriction
(.-) :: KnownSymbol l => Rec r -> Label l -> Rec (r :- l) infix 8 Source
Record restriction. Delete the label l from the record.
Update
update :: KnownSymbol l => Label l -> (r :! l) -> Rec r -> Rec r Source
Update the value associated with the label.
Query
Combine
Union
Disjoint union
Row constraints
type (:\) r l = LacksP l r ~ LabelUnique l Source
Does the row lack (i.e. it has not) the specified label?
type Disjoint l r = DisjointR l r ~ IsDisjoint Source
Are the two rows disjoint? (i.e. their sets of labels are disjoint)
If the constaint c
holds for all elements in the row r
,
then the methods in this class are available.
rinit :: CWit c -> (forall a. c a => a) -> Rec r Source
Given a default value a
, wherea
can be instantiated to each type in the row,
create a new record in which all elements carry this default value.
erase :: CWit c -> (forall a. c a => a -> b) -> Rec r -> [b] Source
Given a function (a -> b)
where a
can be instantiated to each type in the row,
apply the function to each element in the record and collect the result in a list.
eraseZip :: CWit c -> (forall a. c a => a -> a -> b) -> Rec r -> Rec r -> [b] Source
Given a function (a -> a -> b)
where a
can be instantiated to each type of the row,
apply the function to each pair of values that can be obtained by indexing the two records
with the same label and collect the result in a list.
A witness of a constraint. For use like this rinit (CWit :: CWit Bounded) minBound
Row only operations
Syntactic sugar
data RecOp c rowOp where Source
Here we provide a datatype for denoting record operations. Use .|
to
actually apply the operations.
This allows us to chain operations with nicer syntax. For example we can write:
p :<-| z .| y :<- 'b' .| z :!= False .| x := 2 .| y := 'a' .| empty
Which results in:
{ p=False, x=2, y='b' }
Without this sugar, we would have written it like this:
rename z p $ update y 'b' $ extendUnique z False $ extend x 2 $ extend y 'a' empty
:<-
- Record update. Sugar for
update
. :=
- Record extension. Sugar for
extend
. :!=
- Record extension, without shadowing. Sugar for
extendUnique
. :<-|
- Record label renaming. Sugar for
rename
. :<-!
- Record label renaming. Sugar for
renameUnique
.
(:<-) :: KnownSymbol l => Label l -> a -> RecOp (HasType l a) RUp infix 5 | |
(:=) :: KnownSymbol l => Label l -> a -> RecOp NoConstr (l ::= a) infix 5 | |
(:!=) :: KnownSymbol l => Label l -> a -> RecOp (Lacks l) (l ::= a) infix 5 | |
(:<-|) :: (KnownSymbol l, KnownSymbol l') => Label l' -> Label l -> RecOp NoConstr (l' ::<-| l) | |
(:<-!) :: (KnownSymbol l, KnownSymbol l', r :\ l') => Label l' -> Label l -> RecOp (Lacks l') (l' ::<-| l) |
Type level datakind corresponding to RecOp
.
Here we provide a datatype for denoting row operations. Use :|
to
actually apply the type level operation.
This allows us to chain type level operations with nicer syntax. For example we can write:
"p" ::<-| "z" :| RUp :| "z" ::= Bool :| "x" ::= Double :| "y" ::= Char :| Empty
As the type of the expression:
p :<-| z .| y :<- 'b' .| z :!= False .| x := 2 .| y := 'a' .| empty
Without this sugar, we would have written it like this:
Rename "p" "z" (Extend "z" Bool (Extend x Double (Extend "x" Int Empty)))
Of course, we can also just write:
"p" ::= Bool :| "x" ::= Double :| "y" ::= Int :| Empty
instead, doing the computation ourselves, or even let the type infer.