Safe Haskell | None |
---|
- Heterogeneous type sequences
- Basic list functions
- Reversing HLists
- A nicer notation for lists
- folds
- unfold
- replicate
- concat
- traversing HLists
- Type-level equality for lists (
HEq
) - Ensure a list to contain HNats only
- Membership tests
- Staged equality for lists
- Find an element in a set based on HEq
- Intersection based on HTMember
- Turn a heterogeneous list into a homogeneous one
- With
HMaybe
- Annotated lists
- Splitting by HTrue and HFalse
The HList library
(C) 2004, Oleg Kiselyov, Ralf Laemmel, Keean Schupke
Basic declarations for typeful heterogeneous lists.
Excuse the unstructured haddocks: while there are many declarations here some are alternative implementations should be grouped, and the definitions here are analgous to many list functions in the Prelude.
- data HList l where
- data HNil' = HNil'
- data HCons' a b = HCons' a b
- class UnPrime (Prime a) ~ a => ConvHList a where
- hHead :: HList (e : l) -> e
- hTail :: HList (e : l) -> HList l
- type family HLength x :: HNat
- hLength :: HList l -> Proxy (HLength l)
- type family HAppendList l1 l2 :: [k]
- hAppendList :: HList l1 -> HList l2 -> HList (HAppendList l1 l2)
- append' :: [a] -> [a] -> [a]
- hAppend' :: HFoldr FHCons v l r => HList l -> v -> r
- data FHCons = FHCons
- type family HRevApp l1 l2 :: [k]
- hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
- hReverse :: HList l1 -> HList (HRevApp * l1 ([] *))
- hEnd :: HList l -> HList l
- hBuild :: HBuild' `[]` r => r
- class HBuild' l r where
- class HFoldr f v l r where
- class HScanr f z ls rs where
- class HFoldr1 f l r where
- class HFoldl f z xs r where
- hUnfold :: (Apply p s, HUnfold' p (ApplyR p s)) => p -> s -> HList (HUnfold p s)
- type HUnfold p s = HUnfoldR p (ApplyR p s)
- class HUnfold' p res where
- class HLength (HReplicateR n e) ~ n => HReplicate n e where
- hReplicate :: Proxy n -> e -> HList (HReplicateR n e)
- type family HReplicateR n e :: [k]
- class HConcat a where
- type family UnHList a :: [*]
- hMap :: (HMapAux f as' bs', SameLength' * * as' bs', SameLength' * * bs' as') => f -> HList as' -> HList bs'
- newtype HMap f = HMap f
- type HMapCxt f as bs as' bs' = (HMapAux f as' bs', as ~ HList as', bs ~ HList bs', SameLength as' bs')
- class SameLength' es1 es2
- class (SameLength' x y, SameLength' y x) => SameLength x y
- class HMapAux f l r where
- hMapAux :: SameLength l r => f -> HList l -> HList r
- newtype MapCar f = MapCar f
- hMapMapCar :: HFoldr (MapCar f) (HList `[]`) l l' => f -> HList l -> l'
- hComposeList :: HFoldr Comp (a -> a) l (t -> a) => HList l -> t -> a
- class (Applicative m, SameLength a b) => HSequence m a b | a -> b, m b -> a where
- newtype LiftA2 f = LiftA2 f
- newtype Mapcar f = Mapcar f
- type HMapOut f l e = HFoldr (Mapcar f) [e] l [e]
- hMapOut :: forall f e l. HMapOut f l e => f -> HList l -> [e]
- hMapM :: (Monad m, HMapOut f l (m e)) => f -> HList l -> [m e]
- hMapM_ :: (Monad m, HMapOut f l (m ())) => f -> HList l -> m ()
- type family HNats l :: [HNat]
- hNats :: HList l -> Proxy (HNats l)
- class HMember e1 l b | e1 l -> b
- class HMember' b0 e1 l b | b0 e1 l -> b
- type family HMemberP pred e1 l :: Bool
- type family HMemberP' pred e1 l pb :: Bool
- hMember :: HMember e l b => Proxy e -> Proxy l -> Proxy b
- class HMemberM e1 l r | e1 l -> r
- class HMemberM1 b e1 l r | b e1 l -> r
- class HMemberM2 b e1 l r | b e1 l -> r
- class HFind e l n | e l -> n
- class HFind' b e l n | b e l -> n
- class HTMember e l b | e l -> b
- hTMember :: HTMember e l b => e -> HList l -> Proxy b
- class HTIntersect l1 l2 l3 | l1 l2 -> l3 where
- hTIntersect :: HList l1 -> HList l2 -> HList l3
- class HTIntersectBool b h t l1 l2 | b h t l1 -> l2 where
- hTIntersectBool :: Proxy b -> h -> HList t -> HList l1 -> HList l2
- class HList2List l e where
- hList2List :: HList l -> [e]
- class ToHJust l l' | l -> l', l' -> l where
- toHJust2 :: (HMapAux (HJust ()) as' bs', SameLength' * * as' bs', SameLength' * * bs' as') => HList as' -> HList bs'
- class FromHJust l where
- type FromHJustR l :: [*]
- fromHJust :: HList l -> HList (FromHJustR l)
- fromHJust2 :: (HMapAux HFromJust as' bs', SameLength' * * as' bs', SameLength' * * bs' as') => HList as' -> HList bs'
- data HFromJust = HFromJust
- data HAddTag t = HAddTag t
- data HRmTag = HRmTag
- hAddTag :: (HMapAux (HAddTag t) as' bs', SameLength' * * as' bs', SameLength' * * bs' as') => t -> HList as' -> HList bs'
- hRmTag :: (HMapAux HRmTag as' bs', SameLength' * * as' bs', SameLength' * * bs' as') => HList as' -> HList bs'
- hFlag :: (HMapAux (HAddTag (Proxy Bool True)) as' bs', SameLength' * * as' bs', SameLength' * * bs' as') => HList as' -> HList bs'
- class HSplit l where
Heterogeneous type sequences
The easiest way to ensure that sequences can only be formed with Nil and Cons is to use GADTs The kind [*] is list kind (lists lifted to types)
(HEq * e1 e b, HDeleteManyCase * b e1 e l l1) => HDeleteMany * e1 (HList (: * e l)) (HList l1) | |
HDeleteMany k e (HList ([] *)) (HList ([] *)) | |
(HOccurs e l, HProject l (HList l')) => HProject l (HList (: * e l')) | |
(HOccurrence e (: * x y) l', HOccurs' e l') => HOccurs e (HList (: * x y)) | |
HExtend e (HList l) | |
~ [*] l' (HRevApp * l ([] *)) => HBuild' l (HList l') | |
(ConvHList l, Eq (Prime l)) => Eq (HList l) | this comparison is two traversals |
(Data x, Data (HList xs), Typeable (HList (: * x xs)), TypeablePolyK [*] (: * x xs)) => Data (HList (: * x xs)) | |
TypeablePolyK [*] ([] *) => Data (HList ([] *)) | |
(Show e, Show (HList l)) => Show (HList (: * e l)) | |
Show (HList ([] *)) | |
TypeRepsList (Record xs) => Typeable (HList xs) | |
HProject (HList l) (HList ([] *)) | |
HAppend (HList l1) (HList l2) | |
ApplyAB f e e' => ApplyAB (MapCar f) (e, HList l) (HList (: * e' l)) | |
Apply (FHUProj sel ns) (HList l, Proxy HNat (HSucc n)) => Apply (Proxy Bool False, FHUProj sel ns) (HList (: * e l), Proxy HNat n) | |
Apply (Proxy Bool True, FHUProj sel ns) (HList (: * e l), Proxy HNat n) | |
(~ * ch (Proxy Bool (HBoolEQ sel (KMember n ns))), Apply (ch, FHUProj sel ns) (HList (: * e l), Proxy HNat n)) => Apply (FHUProj sel ns) (HList (: * e l), Proxy HNat n) | |
Apply (FHUProj sel ns) (HList ([] *), n) | |
(~ * (HList (: * x y)) z, HZip3 xs ys zs) => HZip3 (: * x xs) (: * (HList y) ys) (: * z zs) |
Alternative representation
HNil' and HCons' are the older ADT-style. This has some advantages over the GADT:
- lazy pattern matches are allowed
- type inference is better if you want to directly pattern match see stackoverflow post here
HCons' a b |
(Eq a, Eq b) => Eq (HCons' a b) | |
(TypeRepsList xs, Typeable x) => TypeRepsList (HCons' x xs) |
Basic list functions
Append
type family HAppendList l1 l2 :: [k]Source
hAppendList :: HList l1 -> HList l2 -> HList (HAppendList l1 l2)Source
the same as hAppend
Alternative append
Historical append
The original HList code is included below. In both cases we had to program the algorithm twice, at the term and the type levels.
The class HAppend
class HAppend l l' l'' | l l' -> l'' where hAppend :: l -> l' -> l''
The instance following the normal append
instance HList l => HAppend HNil l l where hAppend HNil l = l instance (HList l, HAppend l l' l'') => HAppend (HCons x l) l' (HCons x l'') where hAppend (HCons x l) l' = HCons x (hAppend l l')
Reversing HLists
A nicer notation for lists
hEnd :: HList l -> HList lSource
Note:
x :: HList a
- means:
forall a. x :: HList a
hEnd x
- means:
exists a. x :: HList a
List termination
examples
The classes above allow the third (shortest) way to make a list (containing a,b,c) in this case
list = a `HCons` b `HCons` c `HCons` HNil list = a .*. b .*. c .*. HNil list = hEnd $ hBuild a b c
>>>
let x = hBuild True in hEnd x
H[True]
>>>
let x = hBuild True 'a' in hEnd x
H[True, 'a']
>>>
let x = hBuild True 'a' "ok" in hEnd x
H[True, 'a', "ok"]
historical
the show instance has since changed, but these uses of 'hBuild'/'hEnd' still work
HList> let x = hBuild True in hEnd x HCons True HNil
HList> let x = hBuild True 'a' in hEnd x HCons True (HCons 'a' HNil)
HList> let x = hBuild True 'a' "ok" in hEnd x HCons True (HCons 'a' (HCons "ok" HNil))
HList> hEnd (hBuild (Key 42) (Name "Angus") Cow (Price 75.5)) HCons (Key 42) (HCons (Name "Angus") (HCons Cow (HCons (Price 75.5) HNil)))
HList> hEnd (hBuild (Key 42) (Name "Angus") Cow (Price 75.5)) == angus True
folds
foldr
Consume a heterogenous list. GADTs and type-classes mix well
foldl
class HFoldl f z xs r whereSource
like foldl
>>>
hFoldl (uncurry $ flip (:)) [] (1 `HCons` 2 `HCons` HNil)
[2,1]
unfold
Produce a heterogenous list. Uses the more limited
Apply
instead of App
since that's all that is needed for uses of this
function downstream. Those could in principle be re-written.
replicate
class HLength (HReplicateR n e) ~ n => HReplicate n e whereSource
hReplicate :: Proxy n -> e -> HList (HReplicateR n e)Source
HReplicate HZero e | |
HReplicate n e => HReplicate (HSucc n) e |
type family HReplicateR n e :: [k]Source
would be associated with HReplicate
except we want
it to work with e
of any kind, not just *
that you can
put into a HList. An "inverse" of HLength
concat
traversing HLists
producing HList
map
It could be implemented with hFoldr
, as we show further below
hMap :: (HMapAux f as' bs', SameLength' * * as' bs', SameLength' * * bs' as') => f -> HList as' -> HList bs'Source
hMap is written such that the length of the result list can be determined from the length of the argument list (and the other way around). Similarly, the type of the elements of the list is propagated in both directions too.
Excuse the ugly types printed. Unfortunately ghc (still?)
shows types like '[a,b]
using the actual constructors involved
(':) a ((':) b '[])
(or even worse when the kind variables are printed).
>>>
:set -XNoMonomorphismRestriction
>>>
let xs = 1 .*. 'c' .*. HNil
>>>
:t hMap (HJust ()) xs
hMap (HJust ()) xs :: Num y => HList ((':) * (HJust y) ((':) * (HJust Char) ('[] *)))
These 4 examples show that the constraint on the length (2 in this cae)
can be applied before or after the hMap
. That inference is independent of the
direction that type information is propagated for the individual elements.
>>>
let asLen2 xs = xs `asTypeOf` (undefined :: HList '[a,b])
>>>
let lr xs = asLen2 (applyAB (HMap HRead) xs)
>>>
let ls xs = asLen2 (applyAB (HMap HShow) xs)
>>>
let rl xs = applyAB (HMap HRead) (asLen2 xs)
>>>
let sl xs = applyAB (HMap HShow) (asLen2 xs)
>>>
:t lr
lr :: (Read y, Read y1) => HList ((':) * String ((':) * String ('[] *))) -> HList ((':) * y ((':) * y1 ('[] *)))
>>>
:t rl
rl :: (Read y, Read y1) => HList ((':) * String ((':) * String ('[] *))) -> HList ((':) * y ((':) * y1 ('[] *)))
>>>
:t ls
ls :: (Show y, Show y1) => HList ((':) * y ((':) * y1 ('[] *))) -> HList ((':) * String ((':) * String ('[] *)))
>>>
:t sl
sl :: (Show y, Show y1) => HList ((':) * y ((':) * y1 ('[] *))) -> HList ((':) * String ((':) * String ('[] *)))
type HMapCxt f as bs as' bs' = (HMapAux f as' bs', as ~ HList as', bs ~ HList bs', SameLength as' bs')Source
class SameLength' es1 es2 Source
Ensure two lists have the same length. We do case analysis on the first one (hence the type must be known to the type checker). In contrast, the second list may be a type variable.
~ [k1] es2 ([] k1) => SameLength' k k1 ([] k) es2 | |
(SameLength' k k1 xs ys, ~ [k1] es2 (: k1 y ys)) => SameLength' k k1 (: k x xs) es2 |
class (SameLength' x y, SameLength' y x) => SameLength x y Source
symmetrical version of SameLength'
. Written as a class instead of
type SameLength a b = (SameLength' a b, SameLength' b a)
since ghc expands type synonyms, but not classes (and it seems to have the same result)
(SameLength' k1 k x y, SameLength' k k1 y x) => SameLength k k1 x y |
class HMapAux f l r whereSource
hMapAux :: SameLength l r => f -> HList l -> HList rSource
HMapAux f ([] *) ([] *) | |
(ApplyAB f e e', HMapAux f l l', SameLength * * l l') => HMapAux f (: * e l) (: * e' l') |
alternative implementation
currently broken
MapCar f |
hMapMapCar :: HFoldr (MapCar f) (HList `[]`) l l' => f -> HList l -> l'Source
Same as hMap
only a different implementation.
appEndo . mconcat . map Endo
hComposeList :: HFoldr Comp (a -> a) l (t -> a) => HList l -> t -> aSource
>>>
let xs = length .*. (+1) .*. (*2) .*. HNil
>>>
hComposeList xs "abc"
8
sequence
class (Applicative m, SameLength a b) => HSequence m a b | a -> b, m b -> a whereSource
A heterogeneous version of
sequenceA :: (Applicative m) => [m a] -> m [a]
Only now we operate on heterogeneous lists, where different elements
may have different types a
.
In the argument list of monadic values (m a_i),
although a_i may differ, the monad m
must be the same for all
elements. That's why we needed Data.HList.TypeCastGeneric2 (currently (~)).
The typechecker will complain
if we attempt to use hSequence on a HList of monadic values with different
monads.
The hSequence
problem was posed by Matthias Fischmann
in his message on the Haskell-Cafe list on Oct 8, 2006
http://www.haskell.org/pipermail/haskell-cafe/2006-October/018708.html
http://www.haskell.org/pipermail/haskell-cafe/2006-October/018784.html
Maybe
>>>
hSequence $ Just (1 :: Integer) `HCons` (Just 'c') `HCons` HNil
Just H[1, 'c']
>>>
hSequence $ return 1 `HCons` Just 'c' `HCons` HNil
Just H[1, 'c']
List
>>>
hSequence $ [1] `HCons` ['c'] `HCons` HNil
[H[1, 'c']]
Applicative m => HSequence m ([] *) ([] *) | |
(~ (* -> *) m1 m, Applicative m, HSequence m as bs) => HSequence m (: * (m1 a) as) (: * a bs) |
LiftA2 f |
(ApplyAB f (x, y) z, ~ * mz (m z), ~ * mxy (m x, m y), Applicative m) => ApplyAB (LiftA2 f) mxy mz |
alternative implementation
producing homogenous lists
map (no sequencing)
This one we implement via hFoldr
Mapcar f |
mapM
hMapM :: (Monad m, HMapOut f l (m e)) => f -> HList l -> [m e]Source
mapM :: forall b m a. (Monad m) => (a -> m b) -> [a] -> m [b]
Likewise for mapM_.
See hSequence
if the result list should also be heterogenous.
hMapM_ :: (Monad m, HMapOut f l (m ())) => f -> HList l -> m ()Source
GHC doesn't like its own type. hMapM_ :: forall m a f e. (Monad m, HMapOut f a (m e)) => f -> a -> m () Without explicit type signature, it's Ok. Sigh. Anyway, Hugs does insist on a better type. So we restrict as follows:
Type-level equality for lists (HEq
)
Ensure a list to contain HNats only
type family HNats l :: [HNat]Source
We do so constructively, converting the HList whose elements are Proxy HNat to [HNat]. The latter kind is unpopulated and is present only at the type level.
Membership tests
class HMember e1 l b | e1 l -> bSource
Check to see if an HList contains an element with a given type This is a type-level only test
Another type-level membership test
class HMemberM e1 l r | e1 l -> rSource
Check to see if an element e occurs in a list l If not, return 'Nothing If the element does occur, return 'Just l1 where l1 is a type-level list without e
Staged equality for lists
removed. use Typeable instead
Find an element in a set based on HEq
Membership test based on type equality
Intersection based on HTMember
class HTIntersect l1 l2 l3 | l1 l2 -> l3 whereSource
HTIntersect ([] *) l ([] *) | |
(HTMember * h l1 b, HTIntersectBool b h t l1 l2) => HTIntersect (: * h t) l1 l2 |
class HTIntersectBool b h t l1 l2 | b h t l1 -> l2 whereSource
HTIntersect t l1 l2 => HTIntersectBool False h t l1 l2 | |
HTIntersect t l1 l2 => HTIntersectBool True h t l1 (: * h l2) |
Turn a heterogeneous list into a homogeneous one
class HList2List l e whereSource
Same as hMapOut Id
hList2List :: HList l -> [e]Source
HList2List ([] *) e | |
HList2List l e => HList2List (: * e l) e |
With HMaybe
Turn list in a list of justs
class ToHJust l l' | l -> l', l' -> l whereSource
the same as map Just
>>>
toHJust (2 .*. 'a' .*. HNil)
H[HJust 2, HJust 'a']
>>>
toHJust2 (2 .*. 'a' .*. HNil)
H[HJust 2, HJust 'a']
toHJust2 :: (HMapAux (HJust ()) as' bs', SameLength' * * as' bs', SameLength' * * bs' as') => HList as' -> HList bs'Source
alternative implementation. The Apply instance is in Data.HList.FakePrelude. A longer type could be inferred. toHJust2 :: (HMap' (HJust ()) a b) => HList a -> HList b
Extract justs from list of maybes
type FromHJustR l :: [*]Source
fromHJust :: HList l -> HList (FromHJustR l)Source
alternative implementation
fromHJust2 :: (HMapAux HFromJust as' bs', SameLength' * * as' bs', SameLength' * * bs' as') => HList as' -> HList bs'Source
A longer type could be inferred. fromHJust2 :: (HMap' HFromJust a b) => HList a -> HList b
Annotated lists
hAddTag :: (HMapAux (HAddTag t) as' bs', SameLength' * * as' bs', SameLength' * * bs' as') => t -> HList as' -> HList bs'Source
hRmTag :: (HMapAux HRmTag as' bs', SameLength' * * as' bs', SameLength' * * bs' as') => HList as' -> HList bs'Source
hFlag :: (HMapAux (HAddTag (Proxy Bool True)) as' bs', SameLength' * * as' bs', SameLength' * * bs' as') => HList as' -> HList bs'Source
Annotate list with a type-level Boolean hFlag :: HMap' (HAddTag (Proxy True)) l r => HList l -> HList r