HList- Heterogeneous lists

Copyright(C) 2004, Oleg Kiselyov, Ralf Laemmel, Keean Schupke
Safe HaskellNone




The HList library

This module re-exports everything needed to use HList.


Faking dependent types in Haskell

Functions for all collections

Array-like access to HLists

Result-type-driven operations

Type-indexed operations


quasiquoter pun helps to avoid needing a proxy value with type Label in the first place: when you take values out of or into records with pattern matching, the variable name determines the label name.

Unpacked / Unboxed Records

data RecordU l Source

A type which behaves similarly to Record, except all elements must fit in the same UArray. A consequence of this is that RecordU has the following properties:

  • it is strict in the element types
  • it cannot do type-changing updates of RecordU, except if the function applies to all elements
  • it probably is slower to update the very first elements of the RecordU

The benefit is that lookups should be faster and records should take up less space. However benchmarks done with a slow HNat2Integral do not suggest that RecordU is faster than Record.


(ApplyAB f (GetElemTy x) (GetElemTy y), IArray UArray (GetElemTy y), IArray UArray (GetElemTy x)) => HMapAux RecordU f x y 
((~) [*] r r', (~) * v (GetElemTy r), HFindLabel k l r n, HNat2Integral n, IArray UArray v, HasField k l (Record r') v) => HUpdateAtLabel k RecordU l v r r' 
((~) [*] s t, (~) * a b, IArray UArray a, (~) * a (GetElemTy s), HLensCxt k x RecordU s t a b) => Labelable k x RecordU s t a b

make a Lens' (RecordU s) a

(IArray UArray v, (~) * v (GetElemTy ls), HFindLabel k l ls n, HNat2Integral n) => HasField k l (RecordU ls) v 
(RecordValues lv, HList2List (RecordValuesR lv) v, HFindMany * (LabelsOf lv) (LabelsOf r) ixs, IArray UArray v, (~) * v (GetElemTy r), HNats2Integrals ixs) => HUpdateMany lv (RecordU r) 
Eq (UArray Int (GetElemTy l)) => Eq (RecordU l) 
Ord (UArray Int (GetElemTy l)) => Ord (RecordU l) 
Read (UArray Int (GetElemTy l)) => Read (RecordU l) 
Show (UArray Int (GetElemTy l)) => Show (RecordU l) 
HMapUnboxF xs us => HMapUnboxF ((:) * (HList x) xs) ((:) * (RecordU x) us) 
type LabelableTy RecordU = LabelableLens 

data RecordUS x Source

RecordUS is stored as a HList of RecordU to allow the RecordUS to contain elements of different types, so long all of the types can be put into an unboxed array (UArray).

It is advantageous (at least space-wise) to sort the record to keep elements with the same types elements adjacent. See SortForRecordUS for more details.


(HFindLabel k l r n, HLookupByHNatUS n u (Tagged k l v), HasField k l (Record r) v, RecordUSCxt r u) => HasField k l (RecordUS r) v

works expected. See examples attached to bad.

(RecordUSCxt x u, Show (HList u)) => Show (RecordUS x) 

class SortForRecordUS x x' | x -> x' where Source

Reorders a Record such that the RecordUS made from it takes up less space

Bad has alternating Double and Int fields

>>> bad

4 arrays containing one element each are needed when this Record is stored as a RecordUS

>>> recordToRecordUS bad
RecordUS H[RecordU (array (0,0) [(0,1.0)]),RecordU (array (0,0) [(0,2)]),RecordU (array (0,0) [(0,3.0)]),RecordU (array (0,0) [(0,4)])]

It is possible to sort the record

>>> sortForRecordUS bad

This allows the same content to be stored in two unboxed arrays

>>> recordToRecordUS (sortForRecordUS bad)
RecordUS H[RecordU (array (0,1) [(0,1.0),(1,3.0)]),RecordU (array (0,1) [(0,2),(1,4)])]


SortForRecordUS ([] *) ([] *) 
(HPartitionEq * * EqTagValue x ((:) * x xs) xi xo, SortForRecordUS xo xo', (~) [*] sorted (HAppendListR * xi xo'), HAppendList xi xo') => SortForRecordUS ((:) * x xs) sorted 

class HUpdateMany lv rx where Source

analogous flip //. Similar to .<++., except it is restricted to cases where the left argument holds a subset of elements.


hUpdateMany :: Record lv -> rx -> rx Source


(HLeftUnion lv x lvx, HRLabelSet x, HLabelSet [*] (LabelsOf x), HRearrange (LabelsOf x) lvx x) => HUpdateMany lv (Record x)

implementation in terms of .<++.

(RecordValues lv, HList2List (RecordValuesR lv) v, HFindMany * (LabelsOf lv) (LabelsOf r) ixs, IArray UArray v, (~) * v (GetElemTy r), HNats2Integrals ixs) => HUpdateMany lv (RecordU r) 

hMapRU :: HMapCxt RecordU f x y => f -> RecordU x -> RecordU y Source

hMap specialized to RecordU

internals for types

class HFindMany ls r ns | ls r -> ns Source

behaves like map HFind


HFindMany k ([] k) r ([] HNat) 
(HFind k l r n, HFindMany k ls r ns) => HFindMany k ((:) k l ls) r ((:) HNat n ns) 

class HNats2Integrals ns where Source


hNats2Integrals :: Integral i => Proxy ns -> [i] Source


class RecordUSCxt x u | x -> u, u -> x Source

connect the unpacked x representation with the corresponding list of RecordU u representation.


(HGroupBy * EqTagValue x g, HMapUnboxF g u) => RecordUSCxt x u

the only instance

class HLookupByHNatUS n us e | n us -> e Source

Minimal complete definition



((~) (Either HNat HNat) r (HSubtract (HLength * u) n), (~) * (RecordU u) ru, HLookupByHNatUS1 r n u us e) => HLookupByHNatUS n ((:) * ru us) e 

class HLookupByHNatUS1 r n u us e | r n u us -> e Source

Minimal complete definition



(HNat2Integral n, (~) * (HLookupByHNatR n u) le, (~) * le (Tagged k l e), IArray UArray e, (~) * e (GetElemTy u)) => HLookupByHNatUS1 (Left HNat HNat t) n u us le 
HLookupByHNatUS t us e => HLookupByHNatUS1 (Right HNat HNat t) n u us e 

type family HSubtract n1 n2 :: Either HNat HNat Source

HSubtract a b is Left (a-b), Right (b-a) or Right HZero


class HMapUnboxF xs us | xs -> us, us -> xs Source

proof that hMap UnboxF :: r xs -> r us can determine xs from us and us from xs


HMapUnboxF ([] *) ([] *) 
HMapUnboxF xs us => HMapUnboxF ((:) * (HList x) xs) ((:) * (RecordU x) us) 

data UnboxF Source


((~) * hx (HList x), (~) * ux (RecordU x), RecordToRecordU x) => ApplyAB UnboxF hx ux 

data BoxF Source


((~) * ux (RecordU x), (~) * hx (HList x), RecordUToRecord x) => ApplyAB BoxF ux hx 

data EqTagValue Source


HEqByFn * EqTagValue 
((~) * txv (Tagged k x v), (~) * tyw (Tagged k1 y w), HEq * v w b) => HEqBy * * EqTagValue txv tyw b 

type family GetElemTy x :: * Source


type GetElemTy ((:) * (Tagged k label v) rest) = v 

class ElemTyEq xs Source

all elements of the list have the same type


ElemTyEq ([] *) 
(~) * t1v (Tagged k t v) => ElemTyEq ((:) * t1v rest) 
((~) * t1v (Tagged k t1 v), (~) * t2v (Tagged k1 t2 v), ElemTyEq ((:) * tv2 rest)) => ElemTyEq ((:) * tv1 ((:) * tv2 rest)) 


A subset of Data.HList.HList is re-exported.

A subset of Data.HList.HSort

hSort :: HSort x y => HList x -> HList y Source

type HSort x y = HSortBy HLeFn x y Source

class (SameLength a b, HEqByFn le) => HSortBy le a b | le a -> b where Source

quick sort with a special case for sorted lists


hSortBy :: Proxy le -> HList a -> HList b Source


(SameLength * * a b, HIsAscList k le a ok, HSortBy1 Bool k ok le a b) => HSortBy k le a b 

data HLeFn Source

the "standard" <= for types. Reuses HEqBy

Note that ghc-7.6 is missing instances for Symbol and Nat, so that sorting only works HNat (as used by Data.HList.Label3).


HEqByFn * HLeFn 
(~) Bool ((<=?) x y) b => HEqBy * Nat HLeFn x y b

only in ghc >= 7.7

(HEq Ordering (CmpSymbol x y) GT nb, (~) Bool (HNot nb) b) => HEqBy * Symbol HLeFn x y b

only in ghc >= 7.7

>>> let b1 = Proxy :: HEqBy HLeFn "x" "y" b => Proxy b
>>> :t b1
b1 :: Proxy 'True
>>> let b2 = Proxy :: HEqBy HLeFn "x" "x" b => Proxy b
>>> :t b2
b2 :: Proxy 'True
>>> let b3 = Proxy :: HEqBy HLeFn "y" "x" b => Proxy b
>>> :t b3
b3 :: Proxy 'False
(~) Bool (HLe x y) b => HEqBy * HNat HLeFn x y b 
HEqBy * k HLeFn x y b => HEqBy * * HLeFn (Proxy k x) (Proxy k y) b 
HEqBy * k HLeFn x y b => HEqBy * * HLeFn (Label k x) (Label k y) b 
HEqBy * k HLeFn x y b => HEqBy * * HLeFn (Tagged k x v) (Tagged k y w) b 
(HEqBy * HNat HLeFn n m b, (~) * ns ns') => HEqBy * * HLeFn (Lbl n ns desc) (Lbl m ns' desc') b

Data.HList.Label3 labels can only be compared if they belong to the same namespace.

data HDown a Source

analogous to Down


HEqBy k1 k f y x b => HEqBy * k (HDown k f) x y b 
HEqByFn k a => HEqByFn * (HDown k a) 

class HSetBy (HNeq HLeFn) ps => HSet ps Source


HSetBy * (HNeq * HLeFn) ps => HSet ps 

class HEqByFn lt => HSetBy lt ps Source

Provided the labels involved have an appropriate instance of HEqByFn, it would be possible to use the following definitions:

type HRLabelSet = HSet
type HLabelSet  = HSet


(HSortBy k lt ps ps', HAscList k lt ps') => HSetBy k lt ps 

class HIsSet ps b | ps -> b Source

>>> let xx = Proxy :: HIsSet [Label "x", Label "x"] b => Proxy b
>>> :t xx
xx :: Proxy 'False
>>> let xy = Proxy :: HIsSet [Label "x", Label "y"] b => Proxy b
>>> :t xy
xy :: Proxy 'True


HIsSetBy * (HNeq * HLeFn) ps b => HIsSet ps b 

class HEqByFn lt => HIsSetBy lt ps b | lt ps -> b Source


(HSortBy k lt ps ps', HIsAscList k lt ps' b) => HIsSetBy k lt ps b 

class HEqByFn le => HAscList le ps Source

HAscList le xs confirms that xs is in ascending order, and reports which element is duplicated otherwise.


HAscList0 k le ps ps => HAscList k le ps 

class HEqByFn le => HIsAscList le xs b | le xs -> b Source

HIsAscList le xs b is analogous to

b = all (\(x,y) -> x `le` y) (xs `zip` tail xs)


HEqByFn k le => HIsAscList k le ([] *) True 
(HEqBy k * le x y b1, HIsAscList k le ((:) * y ys) b2, (~) Bool (HAnd b1 b2) b3) => HIsAscList k le ((:) * x ((:) * y ys)) b3 
HEqByFn k le => HIsAscList k le ((:) * x ([] *)) True 

A subset of Data.HList.HCurry

class HLengthEq xs n => HCurry' n f xs r | f xs -> r, r xs -> f, n f -> xs, xs -> n where Source

'curry'/'uncurry' for many arguments and HLists instead of tuples

XXX the last FD xs -> n is needed to make hCompose infer the right types: arguably it shouldn't be needed


hUncurry' :: Proxy n -> f -> HList xs -> r Source

hCurry' :: Proxy n -> (HList xs -> r) -> f Source


HCurry' HZero b ([] *) b 
HCurry' n b xs r => HCurry' (HSucc n) (x -> b) ((:) * x xs) r 

hCurry :: (HCurry' n f xs r, ArityFwd f n, ArityRev f n) => (HList xs -> r) -> f Source

Note: with ghc-7.10 the Arity constraint added here does not work properly with hCompose, so it is possible that other uses of hCurry are better served by hCurry' Proxy.

hUncurry :: (HCurry' n f xs r, ArityFwd f n, ArityRev f n) => f -> HList xs -> r Source

hCompose :: (HCurry' n2 f2 xs1 r, HCurry' n1 f1 xs x, HCurry' n f xsys r, HSplitAt1 ([] *) n1 xsys xs xs1, HAppendList1 * xs xs1 xsys, ArityFwd f2 n2, ArityFwd f1 n1, ArityRev f2 n2, ArityRev f1 n1) => (x -> f2) -> f1 -> f Source

compose two functions that take multiple arguments. The result of the second function is the first argument to the first function. An example is probably clearer:

>>> let f = hCompose (,,) (,)
>>> :t f
f :: ... -> ... -> ... -> ... -> ((..., ...), ..., ...)
>>> f 1 2 3 4

Note: polymorphism can make it confusing as to how many parameters a function actually takes. For example, the first two ids are id :: (a -> b) -> (a -> b) in

>>> (.) id id id 'y'
>>> hCompose id id id 'y'

still typechecks, but in that case hCompose i1 i2 i3 x == i1 ((i2 i3) x) has id with different types than @(.) i1 i2 i3 x == (i1 (i2 i3)) x

Prompted by http://stackoverflow.com/questions/28932054/can-hlistelim-be-composed-with-another-function


Public interface of Data.HList.TIP

data TIP l Source

TIPs are like Record, except element "i" of the list "l" has type Tagged e_i e_i


TypeIndexed Record TIP 
(HZipList (UntagR x) (UntagR y) (UntagR xy), UntagTag x, UntagTag y, UntagTag xy, SameLengths * ((:) [*] x ((:) [*] y ((:) [*] xy ([] [*])))), HTypeIndexed x, HTypeIndexed y, HUnzip TIP x y xy) => HZip TIP x y xy 
(HZipList (UntagR x) (UntagR y) (UntagR xy), UntagTag x, UntagTag y, UntagTag xy, HTypeIndexed x, HTypeIndexed y, SameLengths * ((:) [*] x ((:) [*] y ((:) [*] xy ([] [*]))))) => HUnzip TIP x y xy 
(HDeleteAtLabel k Record e v v', HTypeIndexed v') => HDeleteAtLabel k TIP e v v' 
(HUpdateAtLabel * Record e' e r r', HTypeIndexed r', (~) * e e') => HUpdateAtLabel * TIP e' e r r' 
LabelableTIPCxt x s t a b => Labelable * x TIP s t a b

make a Lens' (TIP s) a.

tipyLens provides a Lens (TIP s) (TIP t) a b, which tends to need too many type annotations to be practical

((~) * e e', HasField * e (Record l) e') => HasField * e (TIP l) e' 
(HOccurs e (TIP l1), SubType * * (TIP l1) (TIP l2)) => SubType * * (TIP l1) (TIP ((:) * e l2)) 
SubType * * (TIP l) (TIP ([] *))

Subtyping for TIPs

HasField * e (Record ((:) * x ((:) * y l))) e => HOccurs e (TIP ((:) * x ((:) * y l))) 
(~) * tee (Tagged * e e) => HOccurs e (TIP ((:) * tee ([] *)))

One occurrence and nothing is left

This variation provides an extra feature for singleton lists. That is, the result type is unified with the element in the list. Hence the explicit provision of a result type can be omitted.

(HRLabelSet ((:) * (Tagged * e e) l), HTypeIndexed l) => HExtend e (TIP l) 
Bounded (HList r) => Bounded (TIP r) 
Eq (HList a) => Eq (TIP a) 
(TypeablePolyK [*] xs, Typeable * (HList xs), Data (HList xs)) => Data (TIP xs) 
Ord (HList r) => Ord (TIP r) 
HMapOut (HComp HShow HUntag) l String => Show (TIP l) 
Ix (HList r) => Ix (TIP r) 
Monoid (HList a) => Monoid (TIP a) 
(HAppend (HList l) (HList l'), HTypeIndexed (HAppendListR * l l')) => HAppend (TIP l) (TIP l') 
Typeable ([*] -> *) TIP 
type LabelableTy TIP = LabelableLens 
type HExtendR e (TIP l) = TIP ((:) * (Tagged * e e) l) 
type HAppendR * (TIP l) (TIP l') = TIP (HAppendListR * l l') 

tipyUpdate :: (HUpdateAtLabel * record v v r r, SameLength' * * r r) => v -> record r -> record r Source

tipyLens :: (HAllTaggedEq t, HAllTaggedEq s, HLabelSet [*] (LabelsOf t), HLabelSet [*] (LabelsOf s), HasField * b (Record t) b, HasField * b (Record s) b, HUpdateAtLabel2 * b b t s, HUpdateAtLabel2 * b b s t, SameLength' * * t s, SameLength' * * s t, SameLabels [*] [*] s t, HAllTaggedLV t, HAllTaggedLV s, Functor f) => (b -> f b) -> TIP s -> f (TIP t) Source

provides a Lens (TIP s) (TIP t) a b

When using set (also known as .~), tipyLens' can address the ambiguity as to which field "a" should actually be updated.

tipyLens' :: (HAllTaggedEq t, HLabelSet [*] (LabelsOf t), HasField * b (Record t) b, HUpdateAtLabel2 * b b t t, SameLength' * * t t, SameLabels [*] [*] t t, HAllTaggedLV t, Functor f) => (b -> f b) -> TIP t -> f (TIP t) Source

provides a Lens' (TIP s) a. hLens' :: Label a -> Lens' (TIP s) a is another option.


tipyProject :: (HAllTaggedEq l, HLabelSet [*] (LabelsOf l), H2ProjectByLabels ls r l b, HAllTaggedLV l) => proxy ls -> TIP r -> TIP l Source

Use Labels to specify the first argument

tipyProject2 :: (HAllTaggedEq l1, HAllTaggedEq l, HLabelSet [*] (LabelsOf l), HLabelSet [*] (LabelsOf l1), H2ProjectByLabels ls r l l1, HAllTaggedLV l1, HAllTaggedLV l) => proxy ls -> TIP r -> (TIP l, TIP l1) Source

The same as tipyProject, except also return the types not requested in the proxy argument

tipyTuple :: (HOccurs l1 (r v1), HOccurs l1 (r v), HOccurs l (r v2), HOccurs l (r v), HDeleteAtLabel * r l1 v1 v', HDeleteAtLabel * r l1 v v2, HDeleteAtLabel * r l v2 v'1, HDeleteAtLabel * r l v v1) => r v -> (l, l1) Source

project a TIP (or HList) into a tuple

tipyTuple' x = (hOccurs x, hOccurs x)

behaves similarly, except tipyTuple excludes the possibility of looking up the same element twice, which allows inferring a concrete type in more situations. For example

(\x y z -> tipyTuple (x .*. y .*. emptyTIP) `asTypeOf` (x, z)) () 'x'

has type Char -> ((), Char). tipyTuple' would need a type annotation to decide whether the type should be Char -> ((), Char) or () -> ((), ())

tipyTuple3 :: (HOccurs l2 (r v5), HOccurs l2 (r v2), HOccurs l2 (r v), HOccurs l1 (r v4), HOccurs l1 (r v1), HOccurs l1 (r v), HOccurs l (r v6), HOccurs l (r v3), HOccurs l (r v), HDeleteAtLabel * r l2 v5 v6, HDeleteAtLabel * r l2 v2 v', HDeleteAtLabel * r l2 v v3, HDeleteAtLabel * r l1 v4 v'1, HDeleteAtLabel * r l1 v1 v2, HDeleteAtLabel * r l1 v v5, HDeleteAtLabel * r l v6 v'2, HDeleteAtLabel * r l v3 v4, HDeleteAtLabel * r l v v1) => r v -> (l, l1, l2) Source

tipyTuple4 :: (HOccurs l3 (r v11), HOccurs l3 (r v7), HOccurs l3 (r v3), HOccurs l3 (r v), HOccurs l2 (r v10), HOccurs l2 (r v6), HOccurs l2 (r v2), HOccurs l2 (r v), HOccurs l1 (r v9), HOccurs l1 (r v5), HOccurs l1 (r v1), HOccurs l1 (r v), HOccurs l (r v12), HOccurs l (r v8), HOccurs l (r v4), HOccurs l (r v), HDeleteAtLabel * r l3 v11 v12, HDeleteAtLabel * r l3 v7 v8, HDeleteAtLabel * r l3 v3 v', HDeleteAtLabel * r l3 v v4, HDeleteAtLabel * r l2 v10 v11, HDeleteAtLabel * r l2 v6 v'1, HDeleteAtLabel * r l2 v2 v3, HDeleteAtLabel * r l2 v v7, HDeleteAtLabel * r l1 v9 v'2, HDeleteAtLabel * r l1 v5 v6, HDeleteAtLabel * r l1 v1 v2, HDeleteAtLabel * r l1 v v10, HDeleteAtLabel * r l v12 v'3, HDeleteAtLabel * r l v8 v9, HDeleteAtLabel * r l v4 v5, HDeleteAtLabel * r l v v1) => r v -> (l, l1, l2, l3) Source

tipyTuple5 :: (HOccurs l4 (r v19), HOccurs l4 (r v14), HOccurs l4 (r v9), HOccurs l4 (r v4), HOccurs l4 (r v), HOccurs l3 (r v18), HOccurs l3 (r v13), HOccurs l3 (r v8), HOccurs l3 (r v3), HOccurs l3 (r v), HOccurs l2 (r v17), HOccurs l2 (r v12), HOccurs l2 (r v7), HOccurs l2 (r v2), HOccurs l2 (r v), HOccurs l1 (r v16), HOccurs l1 (r v11), HOccurs l1 (r v6), HOccurs l1 (r v1), HOccurs l1 (r v), HOccurs l (r v20), HOccurs l (r v15), HOccurs l (r v10), HOccurs l (r v5), HOccurs l (r v), HDeleteAtLabel * r l4 v19 v20, HDeleteAtLabel * r l4 v14 v15, HDeleteAtLabel * r l4 v9 v10, HDeleteAtLabel * r l4 v4 v', HDeleteAtLabel * r l4 v v5, HDeleteAtLabel * r l3 v18 v19, HDeleteAtLabel * r l3 v13 v14, HDeleteAtLabel * r l3 v8 v'1, HDeleteAtLabel * r l3 v3 v4, HDeleteAtLabel * r l3 v v9, HDeleteAtLabel * r l2 v17 v18, HDeleteAtLabel * r l2 v12 v'2, HDeleteAtLabel * r l2 v7 v8, HDeleteAtLabel * r l2 v2 v3, HDeleteAtLabel * r l2 v v13, HDeleteAtLabel * r l1 v16 v'3, HDeleteAtLabel * r l1 v11 v12, HDeleteAtLabel * r l1 v6 v7, HDeleteAtLabel * r l1 v1 v2, HDeleteAtLabel * r l1 v v17, HDeleteAtLabel * r l v20 v'4, HDeleteAtLabel * r l v15 v16, HDeleteAtLabel * r l v10 v11, HDeleteAtLabel * r l v5 v6, HDeleteAtLabel * r l v v1) => r v -> (l, l1, l2, l3, l4) Source

type TagUntag xs = TagUntagFD xs (TagR xs) Source

class SameLength a ta => TagUntagFD a ta | a -> ta, ta -> a where Source

TagR can also be used to avoid redundancy when defining types for TIC and TIP.

 type XShort = TagR [A,B,C,D]
 type XLong = [Tagged A A, Tagged B B, Tagged C C, Tagged D D]

an equivalent FD version, which is slightly better with respect to simplifying types containing type variables (in ghc-7.8 and 7.6): http://stackoverflow.com/questions/24110410/

With ghc-7.10 (http://ghc.haskell.org/trac/ghc/ticket/10009) the FD version is superior to the TF version:

class (UntagR (TagR a) ~ a) => TagUntag a where
    type TagR a :: [*]
    hTagSelf :: HList a -> HList (TagR a)
    hUntagSelf :: HList (TagR a) -> HList a

instance TagUntag '[] where
    type TagR '[] = '[]
    hTagSelf _ = HNil
    hUntagSelf _ = HNil

instance TagUntag xs => TagUntag (x ': xs) where
    type TagR (x ': xs) = Tagged x x ': TagR xs
    hTagSelf (HCons x xs) = Tagged x HCons hTagSelf xs
    hUntagSelf (HCons (Tagged x) xs) = x HCons hUntagSelf xs

type family UntagR (xs :: [*]) :: [*]
type instance UntagR '[] = '[]
type instance UntagR (x ': xs) = Untag1 x ': UntagR xs

Length information should flow backwards

>>> let len2 x = x `asTypeOf` (undefined :: HList '[a,b])
>>> let f = len2 $ hTagSelf (hReplicate Proxy ())
>>> :t f
f :: HList '[Tagged () (), Tagged () ()]


hTagSelf :: HList a -> HList ta Source

hUntagSelf :: HList ta -> HList a Source


TagUntagFD ([] *) ([] *) 
(TagUntagFD xs ys, (~) * txx (Tagged * x x)) => TagUntagFD ((:) * x xs) ((:) * txx ys) 

type family TagR a :: [*] Source


type TagR ([] *) = [] * 
type TagR ((:) * x xs) = (:) * (Tagged * x x) (TagR xs) 

TIP transform

class TransTIP op db where Source

Transforming a TIP: applying to a TIP a (polyvariadic) function that takes arguments from a TIP and updates the TIP with the result.

In more detail: we have a typed-indexed collection TIP and we would like to apply a transformation function to it, whose argument types and the result type are all in the TIP. The function should locate its arguments based on their types, and update the TIP with the result. The function may have any number of arguments, including zero; the order of arguments should not matter.

The problem was posed by Andrew U. Frank on Haskell-Cafe, Sep 10, 2009. http://www.haskell.org/pipermail/haskell-cafe/2009-September/066217.html The problem is an interesting variation of the keyword argument problem.

Examples can be found in examples/TIPTransform.hs and examples/TIPTransformM.hs


ttip :: op -> TIP db -> TIP db Source


(HMember * (Tagged * op op) db b, Arity op n, TransTIP1 b n op db) => TransTIP op db 

class Monad m => TransTIPM m op db where Source

In March 2010, Andrew Frank extended the problem for monadic operations. This is the monadic version of TIPTransform.hs in the present directory.

This is the TF implementation. When specifying the operation to perform over a TIP, we can leave it polymorphic over the monad. The type checker will instantiate the monad based on the context.


ttipM :: op -> TIP db -> m (TIP db) Source


(Monad m, HMember * (Tagged * op op) db b, Arity (m' op) n, TransTIPM1 b n m (m' op) db) => TransTIPM m (m' op) db 


Public interface of Data.HList.TIC

data TIC l Source

A datatype for type-indexed co-products. A TIC is just a Variant, where the elements of the type-level list "l" are in the form Tagged x x.


TypeIndexed Variant TIC 
HMapAux Variant f xs ys => HMapAux TIC f xs ys 
(TICPrism s t a b, (~) * x a, (~) * a b, (~) [*] s t, SameLength * * s t) => Labelable * x TIC s t a b
hLens' :: Label a -> Prism' (TIC s) a

note that a more general function ticPrism :: Prism (TIC s) (TIC t) a b, cannot have an instance of Labelable

HasField * o (Variant l) (Maybe o) => HasField * o (TIC l) (Maybe o)

Public destructor (or, open union's projection function)

(HasField * o (TIC l) mo, (~) * mo (Maybe o)) => HOccurs mo (TIC l) 
((~) * me (Maybe e), HOccursNot * (Tagged * e e) l) => HExtend me (TIC l)
Nothing .*. x = x
Just a .*. y = mkTIC a
Bounded (Variant l) => Bounded (TIC l) 
Enum (Variant l) => Enum (TIC l) 
Eq (Variant l) => Eq (TIC l) 
(TypeablePolyK [*] xs, Typeable * (Variant xs), Data (Variant xs)) => Data (TIC xs) 
Ord (Variant l) => Ord (TIC l) 
(ReadVariant l, HAllTaggedEq l, HRLabelSet l) => Read (TIC l) 
ShowVariant l => Show (TIC l)

TICs are not opaque

Ix (Variant l) => Ix (TIC l) 
Monoid (Variant l) => Monoid (TIC l) 
Typeable ([*] -> *) TIC 
type LabelableTy TIC = LabelablePrism 
type HExtendR me (TIC l) = TIC ((:) * (Tagged * (UnMaybe me) (UnMaybe me)) l) 

creating TIC

mkTIC :: (HAllTaggedEq l, HLabelSet [*] (LabelsOf l), HasField * i (Record l) i, HFind1 * i (UnLabel * i (LabelsOf l)) n, HAllTaggedLV l, KnownNat (HNat2Nat n)) => i -> TIC l Source

make a TIC for use in contexts where the result type is fixed

mkTIC1 :: forall i. MkVariant i i `[Tagged i i]` => i -> TIC `[Tagged i i]` Source

make a TIC that contains one element

mkTIC' Source


:: (HTypeIndexed l, MkVariant i i l) 
=> i 
-> proxy l

the ordering of types in the l :: [*] matters. This argument is intended to fix the ordering it can be a Record, Variant, TIP, Proxy

-> TIC l 

Public constructor (or, open union's injection function)


ticPrism :: (TICPrism s t a b, SameLength s t, Choice p, Applicative f) => (a `p` f b) -> TIC s `p` f (TIC t) Source

ticPrism' :: forall s t a b. (HPrism a s t a b, a ~ b, s ~ t) => forall f p. (Applicative f, Choice p) => (a `p` f b) -> TIC s `p` f (TIC t) Source

Prism' (TIC s) a


Public interface of Data.HList.Variant

data Variant vs Source

Variant vs has an implementation similar to Dynamic, except the contained value is one of the elements of the vs list, rather than being one particular instance of Typeable.

>>> v .!. _right
>>> v .!. _left
Just 'x'

In some cases the pun quasiquote works with variants,

>>> let f [pun| left right |] = (left,right)
>>> f v
(Just 'x',Nothing)
>>> f w
(Nothing,Just 5)
>>> let add1 v = hMapV (Fun succ :: Fun '[Enum] '()) v
>>> f (add1 v)
(Just 'y',Nothing)
>>> f (add1 w)
(Nothing,Just 6)


Relabeled Variant 
TypeIndexed Variant TIC 
(ExtendsVariant b t, ProjectVariant s a, ProjectExtendVariant s t, HLeftUnion b s bs, HRLabelSet bs, HRearrange (LabelsOf t) bs t) => Projected Variant s t a b
Prism (Variant s) (Variant t) (Variant a) (Variant b)
HUpdateVariantAtLabelCxt k l e v v' n _e => HUpdateAtLabel k Variant l e v v'
hUpdateAtLabel x e' (mkVariant x e proxy) == mkVariant x e' proxy
hUpdateAtLabel y e' (mkVariant x e proxy) == mkVariant x e  proxy
(HPrism k x s t a b, (~) (* -> * -> *) to (->)) => Labelable k x Variant s t a b

make a Prism (Variant s) (Variant t) a b

(HasField k x (Record vs) a, HFindLabel k x vs n, HNat2Integral n) => HasField k x (Variant vs) (Maybe a) 
(ApplyAB f te te', HMapCxt Variant f ((:) * l ls) ((:) * l' ls')) => HMapAux Variant f ((:) * te ((:) * l ls)) ((:) * te' ((:) * l' ls')) 
ApplyAB f te te' => HMapAux Variant f ((:) * te ([] *)) ((:) * te' ([] *)) 
((~) * le (Tagged k l (Maybe e)), HOccursNot * (Label k l) (LabelsOf v)) => HExtend le (Variant v)

Extension for Variants prefers the first value

(l .=. Nothing) .*. v = v
(l .=. Just e)  .*. _ = mkVariant l e Proxy
(Unvariant ((:) * txy ([] *)) txy, (~) * tx (Tagged k t x), (~) * ty (Tagged k t y), (~) * txy (Tagged k t (x, y))) => HUnzip Variant ((:) * tx ([] *)) ((:) * ty ([] *)) ((:) * txy ([] *)) 
(HUnzip Variant ((:) * x2 xs) ((:) * y2 ys) ((:) * xy2 xys), (~) * tx (Tagged k t x), (~) * ty (Tagged k t y), (~) * txy (Tagged k t (x, y))) => HUnzip Variant ((:) * tx ((:) * x2 xs)) ((:) * ty ((:) * y2 ys)) ((:) * txy ((:) * xy2 xys)) 
(Bounded x, Bounded z, (~) [*] (HRevAppR * ((:) * (Tagged k1 s x) xs) ([] *)) ((:) * (Tagged k t z) sx), MkVariant k t z ((:) * (Tagged k1 s x) xs)) => Bounded (Variant ((:) * (Tagged k s x) xs)) 
Enum x => Enum (Variant ((:) * (Tagged k s x) ([] *)))

While the instances could be written Enum (Variant '[]) Eq/Ord which cannot produce values, so they have instances for empty variants (unsafeEmptyVariant). Enum can produce values, so it is better that fromEnum 0 :: Variant '[] fails with No instance for Enum (Variant '[]) than producing an invalid variant.

(Enum x, Bounded x, Enum (Variant ((:) * y z))) => Enum (Variant ((:) * (Tagged k s x) ((:) * y z)))
>>> let t = minBound :: Variant '[Tagged "x" Bool, Tagged "y" Bool]
>>> [t .. maxBound]
Odd behavior
There are some arguments that this instance should not exist.

The last type in the Variant does not need to be Bounded. This means that enumFrom behaves a bit unexpectedly:

>>> [False .. ]
>>> [t .. ]
[V{x=False},V{x=True},V{y=False},V{y=True},V{y=*** Exception: Prelude.Enum.Bool.toEnum: bad argument

This is a "feature" because it allows an Enum (Variant '[Tagged "a" Bool, Tagged "n" Integer])

Another difficult choice is that the lower bound is fromEnum 0 rather than minBound:

>>> take 5 [ minBound :: Variant '[Tagged "b" Bool, Tagged "i" Int] .. ]
(Eq (Variant xs), Eq x) => Eq (Variant ((:) * x xs)) 
Eq (Variant ([] *)) 
(Typeable * (Variant v), GfoldlVariant v v, GunfoldVariant v v, VariantConstrs v) => Data (Variant v) 
(Ord x, Ord (Variant xs)) => Ord (Variant ((:) * x xs)) 
Ord (Variant ([] *)) 
ReadVariant v => Read (Variant v)

A corresponding read instance

ShowVariant vs => Show (Variant vs)

Variants are not opaque

(Monoid x, Monoid (Variant ((:) * a b))) => Monoid (Variant ((:) * (Tagged k t x) ((:) * a b)))

XXX check this mappend is legal

(Unvariant ((:) * (Tagged k t x) ([] *)) x, Monoid x) => Monoid (Variant ((:) * (Tagged k t x) ([] *))) 
(SameLength * * s a, ExtendsVariant s a, SameLength * * b t, ExtendsVariant b t) => Rearranged [*] Variant s t a b 
Typeable ([*] -> *) Variant 
type LabelableTy Variant = LabelablePrism 
type HExtendR le (Variant v) = Variant ((:) * (UnMaybe le) v) 

mkVariant Source


:: MkVariant x v vs 
=> Label x

the tag

-> v

value to be stored

-> proxy vs

a helper to fix the ordering and types of the potential values that this variant contains. Typically this will be a Proxy, Record or another Variant

-> Variant vs 

mkVariant1 :: Label k l -> e -> Variant ((:) * (Tagged k l e) ([] *)) Source

castVariant :: (RecordValuesR v ~ RecordValuesR v', SameLength v v') => Variant v -> Variant v' Source

in ghc>=7.8, coerce is probably a better choice

newtype HMapV f Source

Apply a function to all possible elements of the variant


HMapV f 


((~) * vx (Variant x), (~) * vy (Variant y), HMapAux Variant (HFmap f) x y, SameLength * * x y) => ApplyAB (HMapV f) vx vy

apply a function to all values that could be in the variant.

hMapV :: (HMapAux Variant (HFmap f) x y, SameLength' * * y x, SameLength' * * x y) => f -> Variant x -> Variant y Source

shortcut for applyAB . HMapV. hMap is more general

hMapOutV :: forall x y z f. (SameLength x y, HMapAux Variant (HFmap f) x y, Unvariant y z, HMapOutV_gety x z ~ y) => f -> Variant x -> z Source

hMapOutV f = unvariant . hMapV f, except an ambiguous type variable is resolved by HMapOutV_gety

class ZipVariant x y xy | x y -> xy, xy -> x y where Source

Applies to variants that have the same labels in the same order. A generalization of

zipEither :: Either a b -> Either a b -> Maybe (Either (a,a) (b,b))
zipEither (Left a) (Left a') = Just (Left (a,a'))
zipEither (Right a) (Right a') = Just (Right (a,a'))
zipEither _ _ = Nothing

see HZip for zipping other collections


zipVariant :: Variant x -> Variant y -> Maybe (Variant xy) Source


ZipVariant ([] *) ([] *) ([] *) 
((~) * tx (Tagged k t x), (~) * ty (Tagged k t y), (~) * txy (Tagged k t (x, y)), ZipVariant xs ys zs, MkVariant k t (x, y) ((:) * txy zs)) => ZipVariant ((:) * tx xs) ((:) * ty ys) ((:) * txy zs) 

class (SameLength v v', SameLabels v v') => ZipVR fs v v' | fs v -> v' where Source

Apply a record of functions to a variant of values. The functions are selected based on those having the same label as the value.


zipVR_ :: Record fs -> Variant v -> Variant v' Source

zipVR is probably a better choice in most situations, since it requires that fs has one function for every element of v


ZipVR fs ([] *) ([] *) 
((~) * lv (Tagged k l v), (~) * lv' (Tagged k l v'), HMemberM * (Label k l) (LabelsOf fs) b, HasFieldM k l (Record fs) f, (~) * (DemoteMaybe (v -> v) f) (v -> v'), MkVariant k l v' ((:) * lv' rs), ZipVR fs vs rs) => ZipVR fs ((:) * lv vs) ((:) * lv' rs) 

zipVR :: (SameLabels fs v, SameLength fs v, ZipVR fs v v', ZipVRCxt fs v v') => Record fs -> Variant v -> Variant v' Source

>>> let xy = x .*. y .*. emptyProxy
>>> let p = Proxy `asLabelsOf` xy
>>> let vs = [ mkVariant x 1.0 p, mkVariant y () p ]
>>> zipVR (hBuild (+1) id) `map` vs



class (ProjectVariant x yin, ProjectVariant x yout) => SplitVariant x yin yout where Source


splitVariant :: Variant x -> Either (Variant yin) (Variant yout) Source


(ProjectVariant x yin, ProjectVariant x yout, H2ProjectByLabels (LabelsOf yin) x xi xo, HRearrange (LabelsOf yin) xi yin, HRearrange (LabelsOf yout) xo yout, HLeftUnion xi xo xixo, HRearrange (LabelsOf x) xixo x, HAllTaggedLV x, HAllTaggedLV yin, HAllTaggedLV yout) => SplitVariant x yin yout 

class ProjectVariant x y where Source

convert a variant with more fields into one with fewer (or the same) fields.

>>> let ty = Proxy :: Proxy [Tagged "left" Int, Tagged "right" Int]
>>> let l = mkVariant _left 1 ty
>>> let r = mkVariant _right 2 ty
>>> map projectVariant [l, r] :: [Maybe (Variant '[Tagged "left" Int])]
[Just V{left=1},Nothing]

rearrangeVariant = fromJust . projectVariant is one implementation of rearrangeVariant, since the result can have the same fields with a different order:

>>> let yt = Proxy :: Proxy [Tagged "right" Int, Tagged "left" Int]
>>> map projectVariant [l, r] `asTypeOf` [Just (mkVariant _left 0 yt)]
[Just V{left=1},Just V{right=2}]


ProjectVariant x ([] *) 
(ProjectVariant x ys, HasField k t (Variant x) (Maybe y), HOccursNot * (Label k t) (LabelsOf ys), (~) * ty (Tagged k t y)) => ProjectVariant x ((:) * ty ys) 

class (HAllTaggedLV y, HAllTaggedLV x) => ExtendsVariant x y where Source

projectVariant . extendsVariant = Just (when the types match up)

extendVariant is a special case


(HAllTaggedLV x, Unvariant ((:) * le ([] *)) e, MkVariant k l e x, (~) * le (Tagged k l e)) => ExtendsVariant ((:) * le ([] *)) x 
(MkVariant k l e y, (~) * le (Tagged k l e), ExtendsVariant ((:) * b bs) y) => ExtendsVariant ((:) * le ((:) * b bs)) y 

class HAllTaggedLV y => ProjectExtendVariant x y where Source

projectExtendVariant = fmap extendVariant . projectVariant

where intermediate variant is as large as possible. Used to implement Data.HList.Labelable.projected

Note that:

>>> let r = projectExtendVariant (mkVariant1 Label 1 :: Variant '[Tagged "x" Int])
>>> r :: Maybe (Variant '[Tagged "x" Integer])


HAllTaggedLV y => ProjectExtendVariant ([] *) y 
((~) * lv (Tagged k l v), HMemberM * lv y inY, ProjectExtendVariant' inY lv y, ProjectExtendVariant xs y) => ProjectExtendVariant ((:) * lv xs) y 


class (SameLength s t, SameLabels s t) => HPrism x s t a b | x s -> a, x t -> b, x s b -> t, x t a -> s where Source

Make a Prism (Variant s) (Variant t) a b out of a Label.

See Data.HList.Labelable.hLens' is a more overloaded version.

Few type annotations are necessary because of the restriction that s and t have the same labels in the same order, and to get "t" the "a" in "s" is replaced with "b".


hPrism :: (Choice p, Applicative f) => Label x -> p a (f b) -> p (Variant s) (f (Variant t)) Source


(MkVariant k x b t, HasField k x (Variant s) (Maybe a), SameLength * * s t, SameLabels [*] [*] s t, H2ProjectByLabels ((:) * (Label k x) ([] *)) s si so, H2ProjectByLabels ((:) * (Label k x) ([] *)) t ti to, (~) [*] so to, HUpdateAtLabel k Variant x b s t, HUpdateAtLabel k Variant x a t s) => HPrism k x s t a b 

unvarianted :: (Unvariant' s a, Unvariant' t b, SameLabels s t, SameLength s t, Functor f) => (a -> f b) -> Variant s -> f (Variant t) Source

Lens (Variant s) (Variant t) a b

Analogue of Control.Lens.chosen :: Lens (Either a a) (Either b b) a b

unvarianted' :: (HAllEqVal ((:) * (Tagged * () b) s) b1, HAllEqVal s b1, HAllEqVal' ((:) * (Tagged * () b) s), Unvariant1 Bool b1 s b, SameLength' * * s s, SameLabels [*] [*] s s, Functor f) => (b -> f b) -> Variant s -> f (Variant s) Source

Lens' (Variant s) a

where we might have s ~ '[Tagged t1 a, Tagged t2 a]

splitVariant1' :: Variant (x : xs) -> Either x (Variant xs) Source

x ~ Tagged s t


class Unvariant v e | v -> e where Source

Convert a Variant which has all possibilities having the same type into a value of that type. Analogous to either id id.

See also unvariant'


unvariant :: Variant v -> e Source


(Unvariant1 Bool b v e, HAllEqVal v b, HAllEqVal ((:) * (Tagged * () e) v) b) => Unvariant v e 

class Unvariant' v e | v -> e where Source

Similar to unvariant, except type variables in v will be made equal to e if possible. That allows the type of Nothing to be inferred as Maybe Char.

>>> unvariant' $ x .=. Nothing .*. mkVariant1 y 'y'

However, this difference leads to more local error messages (Couldn't match type ‘()’ with ‘Char’), rather than the following with unvariant:

     '[Tagged "left" Char, Tagged "right" ()],
     "must have all values equal to ",


unvariant' :: Variant v -> e Source


(HAllEqVal' ((:) * (Tagged * () e) v), Unvariant v e) => Unvariant' v e 

Conversions between collections

class TypeIndexed r tr | r -> tr, tr -> r where Source

Conversion between type indexed collections (TIC and TIP) and the corresponding collection that has other label types (Variant and Record respectively)

See typeIndexed'


typeIndexed :: forall p f s t a b. (TypeIndexedCxt s t a b, Profunctor p, Functor f) => p (tr (TagR a)) (f (tr (TagR b))) -> p (r s) (f (r t)) Source

Iso (r s) (r t) (tr a) (tr b)

typeIndexed' :: (TypeIndexed r tr, TagUntagFD (RecordValuesR s) (TagR (RecordValuesR s)), HLabelSet [*] (LabelsOf s), RecordValues s, HMapAux HList TaggedFn (RecordValuesR s) s, SameLength' * * (RecordValuesR s) (RecordValuesR s), SameLength' * * s s, SameLabels [*] [*] s s, HAllTaggedLV s, Profunctor p, Coercible [*] (TagR (RecordValuesR s)) s, Functor f) => p (tr (TagR (RecordValuesR s))) (f (tr (TagR (RecordValuesR s)))) -> p (r s) (f (r s)) Source

Iso' (Variant s) (TIC a)
Iso' (Record s) (TIP a)

where s has a type like '[Tagged "x" Int], and a has a type like '[Tagged Int Int].

HList and Record

HList and TIP

tipHList :: (TagUntagFD a1 l, TagUntagFD a ta, Profunctor p, Functor f) => p (HList a) (f (HList a1)) -> p (TIP ta) (f (TIP l)) Source

Iso (TIP (TagR a)) (TIP (TagR b)) (HList a) (HList b)

tipHList' :: (TagUntagFD a ta, Profunctor p, Functor f) => p (HList a) (f (HList a)) -> p (TIP ta) (f (TIP ta)) Source

Iso' (TIP (TagR s)) (HList a)

Record and RecordU

unboxed :: forall x y f p. (Profunctor p, Functor f, RecordToRecordU x, RecordUToRecord y) => (RecordU x `p` f (RecordU y)) -> Record x `p` f (Record y) Source

Iso (Record x) (Record y) (RecordU x) (RecordU y)

unboxed' :: (RecordValues x, HMapAux HList TaggedFn (RecordValuesR x) x, HList2List (RecordValuesR x) (GetElemTy x), HLengthEq1 HNat x n, HLengthEq2 HNat x n, SameLength' * * (HReplicateR * n ()) x, Profunctor p, IArray UArray (GetElemTy x), KnownNat (HNat2Nat n), Functor f) => p (RecordU x) (f (RecordU x)) -> p (Record x) (f (Record x)) Source

Iso' (Record x) (RecordU x)

Record and RecordUS

unboxedS :: (HMapUnboxF g1 u1, HMapUnboxF g u, HConcatFD g1 x1, HMapAux HList UnboxF g u, HMapAux HList BoxF u1 g1, HGroupBy * EqTagValue x1 g1, HGroupBy * EqTagValue x g, SameLength' * * g1 u1, SameLength' * * g u, SameLength' * * u1 g1, SameLength' * * u g, Profunctor p, Functor f) => p (RecordUS x) (f (RecordUS x1)) -> p (Record x) (f (Record x1)) Source

Iso (Record x) (Record y) (RecordUS x) (RecordUS y)

unboxedS' :: (HMapUnboxF g u, HConcatFD g x, HMapAux HList UnboxF g u, HMapAux HList BoxF u g, HGroupBy * EqTagValue x g, SameLength' * * g u, SameLength' * * u g, Profunctor p, Functor f) => p (RecordUS x) (f (RecordUS x)) -> p (Record x) (f (Record x)) Source

Iso' (Record x) (RecordUS x)

Record and Variant

hMaybied :: (VariantToHMaybied v1 x, VariantToHMaybied v r, HFoldr HMaybiedToVariantFs [Variant ([] *)] x [Variant v1], HMapAux HList (HFmap HCastF) x r, SameLength' * * x r, SameLength' * * r x, Choice p, Applicative f) => p (Variant v1) (f (Variant v)) -> p (Record x) (f (Record r)) Source

Prism (Record tma) (Record tmb) (Variant ta) (Variant tb)

see hMaybied'

hMaybied' :: (VariantToHMaybied v x, HFoldr HMaybiedToVariantFs [Variant ([] *)] x [Variant v], HMapAux HList (HFmap HCastF) x x, SameLength' * * x x, Choice p, Applicative f) => p (Variant v) (f (Variant v)) -> p (Record x) (f (Record x)) Source

Prism' (Record tma) (Variant ta)

where tma and tmb are lists like

tma ~ '[Tagged x (Maybe a), Tagged y (Maybe b)]
ta  ~ '[Tagged x        a , Tagged y        b ]

If one element of the record is Just, the Variant will contain that element. Otherwise, the prism fails.


The types work out to define a prism:

l = prism' variantToHMaybied (listToMaybe . hMaybiedToVariants)

but the law: s^?l ≡ Just a ==> l # a ≡ s is not followed, because we could have:

  s, s2 :: Record '[Tagged "x" (Maybe Int), Tagged "y" (Maybe Char)]
  s = hBuild (Just 1) (Just '2')
  s2 = hBuild (Just 1) Nothing

  v :: Variant '[Tagged "x" Int, Tagged "y" Char]
  v = mkVariant (Label :: Label "x") 1 Proxy

So that s^?l == Just v. But l#v == s2 /= s, while the law requires l#v == s. hMaybied avoids this problem by only producing a value when there is only one present.

Newtype wrappers

ticVariant :: (Profunctor p, Functor f) => p (Variant t) (f (Variant l)) -> p (TIC t) (f (TIC l)) Source

Iso (TIC s) (TIC t) (Variant s) (Variant t)

typeIndexed may be more appropriate

ticVariant' :: (Profunctor p, Functor f) => p (Variant t) (f (Variant t)) -> p (TIC t) (f (TIC t)) Source

Iso' (TIC s) (Variant s)

tipRecord :: (Profunctor p, Functor f) => p (Record r) (f (Record l)) -> p (TIP r) (f (TIP l)) Source

Iso (TIP s) (TIP t) (Record s) (Record t)

typeIndexed may be more appropriate

tipRecord' :: (Profunctor p, Functor f) => p (Record r) (f (Record r)) -> p (TIP r) (f (TIP r)) Source

Iso' (TIP (TagR s)) (Record a)


class VariantToHMaybied v r | v -> r, r -> v where Source


VariantToHMaybied ([] *) ([] *) 
(VariantToHMaybied v r, HReplicateF nr ConstTaggedNothing () r, (~) * tx (Tagged k t x), (~) * tmx (Tagged k t (Maybe x))) => VariantToHMaybied ((:) * tx v) ((:) * tmx r) 

data HMaybiedToVariantFs Source


((~) * x (Tagged k t (Maybe e), [Variant v]), (~) * y [Variant ((:) * (Tagged k t e) v)], MkVariant k t e ((:) * (Tagged k t e) v)) => ApplyAB HMaybiedToVariantFs x y 

hMaybiedToVariants :: (HFoldr HMaybiedToVariantFs [Variant []] r [Variant v], VariantToHMaybied v r) => Record r -> [Variant v] Source

Every element of the record that is Just becomes one element in the resulting list. See hMaybied' example types that r and v can take.


the "public" parts. More examples are in the module documentation.

class Kw fn arg_def r where Source

kw takes a HList whose first element is a function, and the rest of the elements are default values. A useful trick is to have a final argument () which is not eaten up by a label (A only takes 1 argument). That way when you supply the () it knows there are no more arguments (?).

>>> data A = A
>>> instance IsKeyFN (A -> a -> b) True
>>> let f A a () = a + 1
>>> let f' = f .*. A .*. 1 .*. HNil
>>> kw f' A 0 ()
>>> kw f' ()


kw :: HList (fn : arg_def) -> r Source


(KW' Bool rflag fn akws arg_def r, (~) * akws (Arg [*] kws ([] *)), ReflectFK' * [*] flag fn kws, IsKeyFN r rflag, IsKeyFN fn flag) => Kw fn arg_def r 

recToKW :: forall a b. (HMapCxt HList TaggedToKW a b, HConcat b) => Record a -> HList (HConcatR b) Source

convert a Record into a list that can supply default arguments for kw

A bit of setup:

>>> :set -XQuasiQuotes
>>> import Data.HList.RecordPuns
>>> let f (_ :: Label "a") a (_ :: Label "b") b () = a `div` b
>>> let a = 2; b = 1; f' = f .*. recToKW [pun| a b |]
>>> kw f' ()
>>> kw f' (Label :: Label "a") 10 ()

class IsKeyFN t flag | t -> flag Source

All our keywords must be registered


(~) Bool False flag => IsKeyFN t flag

overlapping/fallback case

IsKeyFN (Label Symbol s -> a -> b) True

labels that impose no restriction on the type of the (single) argument which follows

>>> let testF (_ :: Label "a") (a :: Int) () = a+1
>>> kw (hBuild testF) (Label :: Label "a") 5 ()
(~) * r (c -> b) => IsKeyFN (K k s c -> r) True

The purpose of this instance is to be able to use the same Symbol (type-level string) at different types. If they are supposed to be the same, then use Label instead of K

>>> let kA = K :: forall t. K "a" t
>>> let testF (K :: K "a" Int) a1 (K :: K "a" Integer) a2 () = a1-fromIntegral a2

therefore the following options works:

>>> kw (hBuild testF) kA (5 :: Int) kA (3 :: Integer) ()
>>> kw (hBuild testF) (K :: K "a" Integer) 3 (K :: K "a" Int) 5 ()

But you cannot leave off all Int or Integer annotations.

data K s c Source


(~) * r (c -> b) => IsKeyFN (K k s c -> r) True

The purpose of this instance is to be able to use the same Symbol (type-level string) at different types. If they are supposed to be the same, then use Label instead of K

>>> let kA = K :: forall t. K "a" t
>>> let testF (K :: K "a" Int) a1 (K :: K "a" Integer) a2 () = a1-fromIntegral a2

therefore the following options works:

>>> kw (hBuild testF) kA (5 :: Int) kA (3 :: Integer) ()
>>> kw (hBuild testF) (K :: K "a" Integer) 3 (K :: K "a" Int) 5 ()

But you cannot leave off all Int or Integer annotations.


there are three options for now. However, there are a couple different styles for the first option here:

GHC supports type-level strings (Symbol), and these can be labels. You can refer to these strings using an unwieldy syntax described below. For example if you want to store a value 5 in a record rec with a field called "x", and then get it out again:

let rec = (Label :: Label "x") .=. 5 .*. emptyRecord

rec .!. (Label :: Label "x")

To avoid that pain, you can have a definition x = Label :: Label "x". and just use x instead of repeating Label :: Label "x" so that a lookup becomes:

rec .!. x

makeLabels6 automates definitions like x = Label :: Label "x".

Instances from Data.HList.Label6

>>> :set -XDataKinds
>>> (Label :: Label "x") .=. (5::Int) .*. emptyRecord
>>> let x = Label :: Label "x"
>>> let r = x .=. (5::Int) .*. emptyRecord
>>> r .!. x

Rather than having the x = Label :: Label "x", the labels generated by makeLabelable also double as lenses for Control.Lens. Here is an example of how much better that is:

>>> :set -XNoMonomorphismRestriction -XDataKinds -XPolyKinds
>>> import Control.Lens
>>> import Data.HList.Labelable
>>> let x = hLens' (Label :: Label "x")
>>> let y = hLens' (Label :: Label "y")

The Label6 method:

>>> let r = (Label :: Label "x") .=. "5" .*. emptyRecord

The Labelable way:

>>> let r2 = x .==. "5" .*. emptyRecord
>>> r ^. x
>>> r2 ^. x
>>> r & x .~ ()

When a field is missing, the error names that field:

>>> :t r^.y
...No instance for (Fail (FieldNotFound "y"))

namespaced labels

labels as any instance of Typeable

template haskell


This modules provide useful instances. A useful application can be found in examples/cmdargs.hs

Overlapping instances are restricted to here


internals exported for type signature purposes

class HAllTaggedEq l Source


HAllTaggedEq ([] *) 
(HAllTaggedEq l, (~) * tee (Tagged k e e')) => HAllTaggedEq ((:) * tee l)