HList-0.3.1.0: Heterogeneous lists

Safe HaskellNone

Data.HList.Record

Contents

Description

The HList library

(C) 2004-2006, Oleg Kiselyov, Ralf Laemmel, Keean Schupke

Extensible records

The three-ish models of labels that go with this module;

These used to work:

Synopsis

labels used for doctests

>>> let x = Label :: Label "x"
>>> let y = Label :: Label "y"
>>> let z = Label :: Label "z"

Records

Labels

Record types as label-value pairs, where label is purely phantom. Thus the run-time representation of a field is the same as that of its value, and the record, at run-time, is indistinguishable from the HList of field values. At run-time, all information about the labels is erased.

newtype LVPair l v Source

Field of label l with value type v Polykinded with respect to l: label may be a symbol, a nat, etc.

Constructors

LVPair 

Fields

valueLVPair :: v
 

Instances

(HRearrange k ls rout (HList r'), ~ * r'' (HList (: * (LVPair k1 l v) r'))) => HRearrange' k1 k l ls (: * (LVPair k1 l v) ([] *)) rout r'' 
HasField' k True l (: * (LVPair k l v) r) v 
(HEq k l l1 b, HasField' k b l (: * (LVPair k l1 v1) r) v) => HasField k l (Record (: * (LVPair k l1 v1) r)) v 
(~ [k] (RecordLabels k r1) ls, HMember k l ls b, UnionSymRec' b r1 (LVPair k l v) r2' ru) => UnionSymRec r1 (: * (LVPair k l v) r2') ru 
(~ [k] (RecordLabels k r) ls, HMember k l ls b, HLeftUnionBool b r (LVPair k l v) r''', HLeftUnion r''' r' r'') => HLeftUnion r (: * (LVPair k l v) r') r'' 
(HMemberM k l1 (: k l ls) b, H2ProjectByLabels' k b (: k l ls) (: * (LVPair k l1 v1) r1) rin rout) => H2ProjectByLabels k (: k l ls) (: * (LVPair k l1 v1) r1) rin rout 
Fail * (ExtraField k1 l) => HRearrange [k] ([] k) (: * (LVPair k1 l v) a) (ExtraField k1 l)

For improved error messages

ShowLabel k sy => Typeable1 (LVPair k sy) 
Eq v => Eq (LVPair k l v) 
(ShowLabel k sy, Typeable x) => Typeable (LVPair k sy x) 
(ShowLabel k l, Show v, ShowComponents r) => ShowComponents (: * (LVPair k l v) r) 
RecordValues r => RecordValues (: * (LVPair k l v) r) 
(HEq k l1 l2 leq, HRLabelSet' k k l1 l2 leq r) => HRLabelSet (: * (LVPair k l1 v1) (: * (LVPair k l2 v2) r)) 
(RecordLabelsStr xs, ShowLabel k x) => RecordLabelsStr (: * (LVPair k x t) xs) 
HRLabelSet (: * (LVPair k l v) r) => HExtend (LVPair k l v) (Record r) 

labelLVPair :: LVPair l v -> Label lSource

Label accessor

newLVPair :: Label l -> v -> LVPair l vSource

(.=.) :: Label l -> v -> LVPair l vSource

Create a value with the given label. Analagous to a data constructor such as Just, Left, or Right. Higher fixity than record-modification operations like (.*.), (.-.), etc. to support expression like the below w/o parentheses:

>>> x .=. "v1" .*. y .=. '2' .*. emptyRecord
Record{x="v1",y='2'}

(.-.) :: H2ProjectByLabels `[l]` r _r' r' => Record r -> Label l -> Record r'Source

Remove a field from a record. At the same level as other record modification options (.*.). Analagous to (\\) in lists.

 record1 .-. label1
 label1 .=. value1 .*.
 label2 .=. value2 .-.
 label2 .*.
 emptyRecord
 label1 .=. value1 .-.
 label1 .*.
 label2 .=. value2 .*.
 emptyRecord
 record1 .*. label1 .=. record2 .!. label1
         .*. label2 .=. record2 .!. label2
         .-. label1

Record

newtype Record r Source

Constructors

Record (HList r) 

Instances

(HEq k l l1 b, HasField' k b l (: * (LVPair k l1 v1) r) v) => HasField k l (Record (: * (LVPair k l1 v1) r)) v 
H2ProjectByLabels k (RecordLabels k r2) r1 r2 rout => SubType * * (Record r1) (Record r2)

Subtyping for records

DataRecordCxt a => Data (Record a) 
ShowComponents r => Show (Record r) 
TypeRepsList (Record xs) => Typeable (Record xs) 
(TypeRepsList (Prime xs), ConvHList xs) => TypeRepsList (Record xs) 
(HRLabelSet (HAppendList r1 r2), HAppend (HList r1) (HList r2)) => HAppend (Record r1) (Record r2)
(.*.)
Add a field to a record. Analagous to (++) for lists.
 record .*. field1
        .*. field2
HRLabelSet (: * (LVPair k l v) r) => HExtend (LVPair k l v) (Record r) 

mkRecord :: HRLabelSet r => HList r -> Record rSource

Build a record

emptyRecord :: Record `[]`Source

Build an empty record

Getting Labels

type family RecordLabels r :: [k]Source

Construct the (phantom) list of labels of the record.

type family LabelsOf ls :: [*]Source

Making this ls::[*] and [k] breaks the MainGhcGeneric1.hs...

Getting Values

class RecordValues r whereSource

Construct the HList of values of the record.

Associated Types

type RecordValuesR r :: [*]Source

Instances

RecordValues ([] *) 
RecordValues r => RecordValues (: * (LVPair k l v) r) 

Operations

Show

A corresponding Show instance exists as

 show x = "Record {" ++ showComponents "" x ++ "}"

class ShowComponents l whereSource

Instances

ShowComponents ([] *) 
(ShowLabel k l, Show v, ShowComponents r) => ShowComponents (: * (LVPair k l v) r) 

class ShowLabel l whereSource

Instances

SingI Symbol x => ShowLabel Symbol x 
Show desc => ShowLabel * (Lbl k * x ns desc)

Equality on labels (descriptions are ignored) Use generic instance

Show label

Delete

hDeleteAtLabel label record

hDeleteAtLabel :: forall l t t1 t2. H2ProjectByLabels `[l]` t t1 t2 => Label l -> Record t -> Record t2Source

Lookup/update

Lens-based setters/getters are popular.

This is a provisional method to make a Lens (Record s) (Record t) a b, out of a Label x. Refer to examples/lens.hs for an example.

hLens :: (Functor f, HFind k l (RecordLabels k r) n, HUpdateAtHNat n (LVPair k l v) r, HasField k l (Record r) v1) => Label k l -> (v1 -> f v) -> Record r -> f (Record (HUpdateAtHNatR n (LVPair k l v) r))Source

Lookup

class HasField l r v | l r -> v whereSource

This is a baseline implementation. We use a helper class, HasField, to abstract from the implementation.

Because hLookupByLabel is so frequent and important, we implement it separately, more efficiently. The algorithm is familiar assq, only the comparison operation is done at compile-time

Methods

hLookupByLabel :: Label l -> r -> vSource

Instances

(HEq k l l1 b, HasField' k b l (: * (LVPair k l1 v1) r) v) => HasField k l (Record (: * (LVPair k l1 v1) r)) v 

class HasField' b l r v | b l r -> v whereSource

Methods

hLookupByLabel' :: Proxy b -> Label l -> HList r -> vSource

Instances

HasField k l (Record r) v => HasField' k False l (: * fld r) v 
HasField' k True l (: * (LVPair k l v) r) v 

(.!.) :: HasField l r v => r -> Label l -> vSource

Lookup a value in a record by its label. Analagous to (!!), the list indexing operation. Highest fixity, like (!!).

>>> :{
let record1 = x .=. 3 .*.
              y .=. 'y' .*.
              emptyRecord
:}
>>> record1 .!. x
3
>>> record1 .!. y
'y'
>>> :{
let r2 = y .=. record1 .!. x .*.
         z .=. record1 .!. y .*.
         emptyRecord
:}
>>> r2
Record{y=3,z='y'}

Note that labels made following Data.HList.Labelable allow using Control.Lens.^. instead.

Update

(.@.) :: (HFind k l (RecordLabels k r) n, HUpdateAtHNat n (LVPair k l v) r) => LVPair k l v -> Record r -> Record (HUpdateAtHNatR n (LVPair k l v) r)Source

Update a field with a particular value. Same fixity as (.*.) so that extensions and updates can be chained. There is no real list analogue, since there is no Prelude defined update.

 label1 .=. value1 .@. record1

hUpdateAtLabel :: forall r l n v. (HFind l (RecordLabels r) n, HUpdateAtHNat n (LVPair l v) r) => Label l -> v -> Record r -> Record (HUpdateAtHNatR n (LVPair l v) r)Source

hUpdateAtLabel label value record

type-preserving versions

(.<.) :: (HFind k l (RecordLabels k r) n, HUpdateAtHNat n (LVPair k l v) r, HasField k l (Record r) v) => LVPair k l v -> Record r -> Record (HUpdateAtHNatR n (LVPair k l v) r)Source

The same as .@., except type preserving. It has the same fixity as (.@.).

hTPupdateAtLabel :: (HFind k l (RecordLabels k r) n, HUpdateAtHNat n (LVPair k l v) r, HasField k l (Record r) v) => Label k l -> v -> Record r -> Record (HUpdateAtHNatR n (LVPair k l v) r)Source

A variation on hUpdateAtLabel: type-preserving update.

We could also say:

 hTPupdateAtLabel l v r = hUpdateAtLabel l v r `asTypeOf` r

Then we were taking a dependency on Haskell's type equivalence. This would also constrain the actual implementation of hUpdateAtLabel.

Rename Label

hRenameLabel :: (H2ProjectByLabels k (: k l ([] k)) t t1 t2, HasField k l (Record t) v, HRLabelSet (: * (LVPair k1 l1 v) t2)) => Label k l -> Label k1 l1 -> Record t -> Record (: * (LVPair k1 l1 v) t2)Source

Rename the label of record

>>> hRenameLabel x y (x .=. () .*. emptyRecord)
Record{y=()}

Projection

It is also an important operation: the basis of many deconstructors -- so we try to implement it efficiently.

hProjectByLabels :: (HRLabelSet a, H2ProjectByLabels ls t a b) => Proxy ls -> Record t -> Record aSource

hProjectByLabels ls r returns r with only the labels in ls remaining

Unions

Left

class HLeftUnion r r' r'' | r r' -> r'' whereSource

Methods

hLeftUnion :: Record r -> Record r' -> Record r''Source

Instances

HLeftUnion r ([] *) r 
(~ [k] (RecordLabels k r) ls, HMember k l ls b, HLeftUnionBool b r (LVPair k l v) r''', HLeftUnion r''' r' r'') => HLeftUnion r (: * (LVPair k l v) r') r'' 

class HLeftUnionBool b r f r' | b r f -> r' whereSource

Methods

hLeftUnionBool :: Proxy b -> Record r -> f -> Record r'Source

Instances

HLeftUnionBool True r f r 
HLeftUnionBool False r f (: * f r) 

(.<++.) :: HLeftUnion r r' r'' => Record r -> Record r' -> Record r''Source

Similar to list append, so give this slightly lower fixity than (.*.), so we can write:

 field1 .=. value .*. record1 .<++. record2

Symmetric

Compute the symmetric union of two records r1 and r2 and return the pair of records injected into the union (ru1, ru2).

To be more precise, we compute the symmetric union type ru of two record types r1 and r2. The emphasis on types is important.

The two records (ru1,ru2) in the result of unionSR have the same type ru, but they are generally different values. Here the simple example: suppose

  r1 = (Label .=. True)  .*. emptyRecord
  r2 = (Label .=. False) .*. emptyRecord

Then unionSR r1 r2 will return (r1,r2). Both components of the result are different records of the same type.

To project from the union ru, use hProjectByLabels. It is possible to project from the union obtaining a record that was not used at all when creating the union.

We do assure however that if unionSR r1 r2 gave (r1u,r2u), then projecting r1u onto the type of r1 gives the value identical to r1. Ditto for r2.

class UnionSymRec r1 r2 ru | r1 r2 -> ru whereSource

Methods

unionSR :: Record r1 -> Record r2 -> (Record ru, Record ru)Source

Instances

UnionSymRec r1 ([] *) r1 
(~ [k] (RecordLabels k r1) ls, HMember k l ls b, UnionSymRec' b r1 (LVPair k l v) r2' ru) => UnionSymRec r1 (: * (LVPair k l v) r2') ru 

Reorder Labels

hRearrange :: (HLabelSet ls, HRearrange ls r (HList r')) => Proxy ls -> Record r -> Record r'Source

Rearranges a record by labels. Returns the record r, rearranged such that the labels are in the order given by ls. (recordLabels r) must be a permutation of ls.

Extension

hExtend, hAppend

(.*.) :: HExtend e l => e -> l -> HExtendR e lSource

Hints for type errors

data DuplicatedLabel l Source

Propery of a proper label set for a record: no duplication of labels

data ExtraField l Source

Constructors

ExtraField 

Instances

Fail * (ExtraField k1 l) => HRearrange [k] ([] k) (: * (LVPair k1 l v) a) (ExtraField k1 l)

For improved error messages

data FieldNotFound l Source

Constructors

FieldNotFound 

Instances

Fail * (FieldNotFound k1 l) => HRearrange' k1 k l ls ([] *) rout (FieldNotFound k1 l)

For improved error messages

Unclassified

Probably internals, that may not be useful

class H2ProjectByLabels ls r rin rout | ls r -> rin rout whereSource

Invariant:

 r === rin `disjoint-union` rout
 labels rin === ls
     where (rin,rout) = hProjectByLabels ls r

Methods

h2projectByLabels :: Proxy ls -> HList r -> (HList rin, HList rout)Source

Instances

H2ProjectByLabels k ([] k) r ([] *) r 
H2ProjectByLabels k (: k l ls) ([] *) ([] *) ([] *) 
(HMemberM k l1 (: k l ls) b, H2ProjectByLabels' k b (: k l ls) (: * (LVPair k l1 v1) r1) rin rout) => H2ProjectByLabels k (: k l ls) (: * (LVPair k l1 v1) r1) rin rout 

class H2ProjectByLabels' b ls r rin rout | b ls r -> rin rout whereSource

Methods

h2projectByLabels' :: Proxy b -> Proxy ls -> HList r -> (HList rin, HList rout)Source

Instances

H2ProjectByLabels k ls r rin rout => H2ProjectByLabels' k (Nothing [k]) ls (: * f r) rin (: * f rout) 
H2ProjectByLabels k ls1 r rin rout => H2ProjectByLabels' k (Just [k] ls1) ls (: * f r) (: * f rin) rout 

class HLabelSet ls Source

Relation between HLabelSet and HRLabelSet

Instances

HLabelSet [k] ([] k) 
(HEq k l1 l2 leq, HLabelSet' k k [k] l1 l2 leq r) => HLabelSet [k] (: k l1 (: k l2 r)) 
HLabelSet [k] (: k x ([] k)) 

class HLabelSet' l1 l2 leq r Source

Instances

Fail * (DuplicatedLabel k l1) => HLabelSet' k k1 k2 l1 l2 True r 
(HLabelSet [k] (: k l2 r), HLabelSet [k] (: k l1 r)) => HLabelSet' k k [k] l1 l2 False r 

class HRLabelSet ps Source

Instances

HRLabelSet ([] *) 
HRLabelSet (: * x ([] *)) 
(HEq k l1 l2 leq, HRLabelSet' k k l1 l2 leq r) => HRLabelSet (: * (LVPair k l1 v1) (: * (LVPair k l2 v2) r)) 

class HRLabelSet' l1 l2 leq r Source

Instances

Fail * (DuplicatedLabel k l1) => HRLabelSet' k k1 l1 l2 True r 
(HRLabelSet (: * (LVPair k1 l2 ()) r), HRLabelSet (: * (LVPair k l1 ()) r)) => HRLabelSet' k k1 l1 l2 False r 

class HRearrange ls r r' whereSource

Helper class for hRearrange

Methods

hRearrange2 :: Proxy ls -> HList r -> r'Source

Instances

~ * (HList ([] *)) r => HRearrange [k] ([] k) ([] *) r 
Fail * (ExtraField k1 l) => HRearrange [k] ([] k) (: * (LVPair k1 l v) a) (ExtraField k1 l)

For improved error messages

(H2ProjectByLabels k (: k l ([] k)) r rin rout, HRearrange' k [k] l ls rin rout (HList r'), ~ * r'' (HList r')) => HRearrange [k] (: k l ls) r r'' 

class HRearrange' l ls rin rout r' whereSource

Helper class 2 for hRearrange

Methods

hRearrange2' :: Proxy l -> Proxy ls -> HList rin -> HList rout -> r'Source

Instances

Fail * (FieldNotFound k1 l) => HRearrange' k1 k l ls ([] *) rout (FieldNotFound k1 l)

For improved error messages

(HRearrange k ls rout (HList r'), ~ * r'' (HList (: * (LVPair k1 l v) r'))) => HRearrange' k1 k l ls (: * (LVPair k1 l v) ([] *)) rout r'' 

class UnionSymRec' b r1 f2 r2' ru | b r1 f2 r2' -> ru whereSource

Methods

unionSR' :: Proxy b -> Record r1 -> f2 -> Record r2' -> (Record ru, Record ru)Source

Instances

(UnionSymRec r1 r2' ru, HExtend f2 (Record ru), ~ * (HExtendR f2 (Record ru)) (Record f2ru)) => UnionSymRec' False r1 f2 r2' f2ru 
(UnionSymRec r1 r2' ru, HasField k l2 (Record ru) v2, HUpdateAtHNat n (LVPair k l2 v2) ru, ~ [*] ru (HUpdateAtHNatR n (LVPair k l2 v2) ru), ~ [k] (RecordLabels k ru) ls, ~ * f2 (LVPair k l2 v2), HFind k l2 ls n) => UnionSymRec' True r1 f2 r2' ru