Safe Haskell | None |
---|---|
Language | Haskell2010 |
- Heterogeneous type sequences
- Basic list functions
- Reversing HLists
- A nicer notation for lists
- folds
- unfolds
- concat
- traversing HLists
- 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
- Convert between heterogeneous lists and homogeneous ones
- With
HMaybe
- Annotated lists
- Splitting by HTrue and HFalse
- Splitting by Length
- Conversion to and from tuples
- partition
- groupBy
- span
- zip
- Monoid instance
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 family HList l
- class HProxiesFD xs pxs | pxs -> xs, xs -> pxs where
- type HProxies xs = HProxiesFD xs (AddProxy xs)
- type family AddProxy xs :: k
- type family DropProxy xs :: k
- data ReadElement = ReadElement
- hHead :: HList (e : l) -> e
- hTail :: HList (e : l) -> HList l
- hLast :: HRevApp l1 ([] *) ((:) * e l) => HList l1 -> e
- class HInit xs where
- type family HLength x :: HNat
- hLength :: HLengthEq l n => HList l -> Proxy n
- type family HAppendListR l1 l2 :: [k]
- class HAppendList l1 l2 where
- hAppendList :: HList l1 -> HList l2 -> HList (HAppendListR l1 l2)
- append' :: [a] -> [a] -> [a]
- hAppend' :: HFoldr FHCons v l r => HList l -> v -> r
- data FHCons = FHCons
- type family HRevAppR l1 l2 :: [k]
- class HRevApp l1 l2 l3 | l1 l2 -> l3 where
- class HReverse xs sx | xs -> sx, sx -> xs where
- hReverse_ :: HRevApp l1 ([] *) l3 => HList l1 -> HList l3
- 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 :: (HUnfoldFD f (ApplyR f a) z, Apply f a) => f -> a -> HList z
- type HUnfold p s = HUnfoldR p (ApplyR p s)
- type family HUnfoldR p res :: [*]
- type HUnfold' p res = HUnfoldFD p (ApplyR p res) (HUnfold p res)
- class HUnfoldFD p res z | p res -> z where
- class HLengthEq es n => HReplicateFD n e es | n e -> es, es -> n where
- hReplicate :: Proxy n -> e -> HList es
- type HReplicate n e = HReplicateFD n e (HReplicateR n e)
- type family HReplicateR n e :: [k]
- class HLengthEq r n => HReplicateF n f z r | r -> n where
- hReplicateF :: HLengthEq r n => Proxy n -> f -> z -> HList r
- class HLengthEq r n => HIterate n f z r where
- type HConcat xs = HConcatFD xs (HConcatR xs)
- hConcat :: HConcat xs => HList xs -> HList (HConcatR xs)
- type family HConcatR a :: [*]
- type family UnHList a :: [*]
- class HConcatFD xxs xs | xxs -> xs where
- class HAppendFD a b ab | a b -> ab where
- newtype HMap f = HMap f
- hMap :: (HMapAux r f a b, SameLength' * * b a, SameLength' * * a b) => f -> r a -> r b
- hMapL :: (HMapAux HList f a b, SameLength' * * b a, SameLength' * * a b) => f -> HList a -> HList b
- newtype HMapL f = HMapL f
- class (SameLength a b, HMapAux r f a b) => HMapCxt r f a b
- class HMapAux r f x y where
- hMapAux :: SameLength x y => f -> r x -> r y
- 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
- hSequence2 :: (HFoldr (LiftA2 FHCons) (f (HList ([] *))) l (f a), Applicative f) => HList l -> f a
- 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 HFind1 e l n => HFind e l n | e l -> n
- class HFind1 e l n | e l -> n
- class HFind2 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 | l -> e where
- hList2List :: HList l -> [e]
- list2HListSuffix :: [e] -> Maybe (HList l, [e])
- list2HList :: HList2List l e => [e] -> Maybe (HList l)
- listAsHList :: (HList2List l1 e1, HList2List l e, Choice p, Applicative f) => p (HList l1) (f (HList l)) -> p [e1] (f [e])
- listAsHList' :: (HList2List l e, Choice p, Applicative f) => p (HList l) (f (HList l)) -> p [e] (f [e])
- class (FromHJustR (ToHJustR l) ~ l) => ToHJust l where
- toHJust2 :: (HMapCxt r (HJust ()) a b, ToHJust a, b ~ ToHJustR a) => r a -> r b
- class (FromHJustR (ToHJustR l) ~ l) => FromHJust l where
- type FromHJustR l :: [*]
- fromHJust :: HList l -> HList (FromHJustR l)
- fromHJust2 :: HMapCxt r HFromJust a b => r a -> r b
- data HFromJust = HFromJust
- data HAddTag t = HAddTag t
- data HRmTag = HRmTag
- hAddTag :: (HMapAux r (HAddTag t) a b, SameLength' * * b a, SameLength' * * a b) => t -> r a -> r b
- hRmTag :: (HMapAux r HRmTag a b, SameLength' * * b a, SameLength' * * a b) => r a -> r b
- hFlag :: (HMapAux r (HAddTag (Proxy Bool True)) a b, SameLength' * * b a, SameLength' * * a b) => r a -> r b
- class HSplit l where
- class (HLengthEq xs n, HAppendList1 xs ys xsys) => HSplitAt n xsys xs ys | n xsys -> xs ys, xs ys -> xsys, xs -> n where
- class HSplitAt1 accum n xsys xs ys | accum n xsys -> xs ys where
- class (SameLength' (HReplicateR n ()) xs, HLengthEq1 xs n, HLengthEq2 xs n) => HLengthEq xs n | xs -> n
- class HLengthEq1 xs n
- class HLengthEq2 xs n | xs -> n
- class HStripPrefix xs xsys ys => HAppendList1 xs ys xsys | xs ys -> xsys, xs xsys -> ys
- class HStripPrefix xs xsys ys | xs xsys -> ys
- class HTuple v t | v -> t, t -> v where
- hToTuple :: HList v -> t
- hFromTuple :: t -> HList v
- hTuple :: (HTuple v1 b, HTuple v a, Profunctor p, Functor f) => p a (f b) -> p (HList v) (f (HList v1))
- hTuple' :: (HTuple v b, Profunctor p, Functor f) => p b (f b) -> p (HList v) (f (HList v))
- class HTails a b | a -> b, b -> a where
- class HInits a b | a -> b, b -> a where
- class HInits1 a b | a -> b, b -> a where
- data FHCons2 x = FHCons2 x
- type family HMapCons x xxs :: [*]
- type family HMapTail xxs :: [*]
- class HPartitionEq f x1 xs xi xo | f x1 xs -> xi xo where
- class HPartitionEq1 b f x1 x xs xi xo | b f x1 x xs -> xi xo where
- class HGroupBy f as gs | f as -> gs, gs -> as where
- class HSpanEqBy f x y fst snd | f x y -> fst snd, fst snd -> y where
- class HSpanEqBy1 f x y i o | f x y -> i o where
- hSpanEqBy1 :: Proxy f -> x -> HList y -> (HList i, HList o)
- class HSpanEqBy2 b f x y ys i o | b f x y ys -> i o where
- class HZipList x y l | x y -> l, l -> x y where
- data ConstMempty = ConstMempty
- data UncurryMappend = UncurryMappend
Heterogeneous type sequences
There are three sensible ways to define HLists:
data HList (l::[*]) where HNil :: HList '[] HCons :: e -> HList l -> HList (e ': l)
This ensures that sequences can only be formed with Nil
and Cons. The argument to HList is a promoted lists (kind [*]
),
which has a more attractive syntax.
Earlier versions of HList used an algebraic data type:
data HCons a b = HCons a b data HNil = HNil
Disadvantages:
- values with types like
HCons Int Double
to be created, which are nonsense to the functions in HList - some recursive functions do not need a class with the GADT. For example:
hInit :: HListGADT (x ': xs) -> HListGADT (HInit (x ': xs)) hInit (HCons x xs@(HCons _ _)) = HCons x (hInit xs) hInit (HCons _ HNil) = HNil type family HInit (xs :: [k]) :: [k]
but without the GADT, hInit
is written as in a class,
which complicates inferred types
Advantages
- lazy pattern matches are allowed, so lazy pattern matching
on a value
undefined :: HList [a,b,c]
can create the spine of the list.hProxies
avoids the use ofundefined
, but a slightly more complicated class context has to be written or inferred. - type inference is better if you want to directly pattern match see stackoverflow post here
- better pattern exhaustiveness checking (as of ghc-7.8)
- standalone deriving works
- Data.Coerce.coerce works because the parameters have role representational, not nominal as they are for the GADT and data family. Probably the GADT/type family actually do have a representational role: http://stackoverflow.com/questions/24222552/does-this-gadt-actually-have-type-role-representational
The data family version (currently used) gives the same type constructor
HList :: [*] -> *
as the GADT, while pattern matching behaves
like the algebraic data type. Furthermore, nonsense values like
HCons 1 2 :: HCons Int Int
cannot be written with the data family.
A variation on the data family version is
data instance HList '[] = HNil newtype instance HList (x ': xs) = HCons1 (x, HList xs) pattern HCons x xs = HCons1 (x, xs)
This allows HList to have a nominal role, but on the other hand the PatternSynonym is not supported with ghc-7.6 and exhaustiveness checking is not as good (warnings for _ being unmatched)
(SameLengths * ((:) [*] x ((:) [*] y ((:) [*] xy ([] [*])))), HZipList x y xy) => HZip HList x y xy | |
(SameLengths * ((:) [*] x ((:) [*] y ((:) [*] xy ([] [*])))), HZipList x y xy) => HUnzip HList x y xy | |
(HDeleteAtHNat n l, HType2HNat * e l n, (~) [*] l' (HDeleteAtHNatR n l)) => HDeleteAtLabel * HList e l l' | should this instead delete the first element of that type? |
(HEq * e1 e b, HDeleteManyCase * b e1 e l l1) => HDeleteMany * e1 (HList ((:) * e l)) (HList l1) | |
HDeleteMany k e (HList ([] *)) (HList ([] *)) | |
HMapAux HList f ([] *) ([] *) | |
(HSpanEqBy k f a as fst snd, HGroupBy k f snd gs) => HGroupBy k f ((:) * a as) ((:) * (HList ((:) * a fst)) gs) | |
(ApplyAB f e e', HMapAux HList f l l', SameLength * * l l') => HMapAux HList f ((:) * e l) ((:) * e' l') | |
(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) | |
HReverse l l' => HBuild' l (HList l') | |
HInits1 a b => HInits a ((:) * (HList ([] *)) b) | |
(Bounded x, Bounded (HList xs)) => Bounded (HList ((:) * x xs)) | |
Bounded (HList ([] *)) | |
(Eq x, Eq (HList xs)) => Eq (HList ((:) * x xs)) | |
Eq (HList ([] *)) | |
(Data x, Data (HList xs), TypeablePolyK [*] ((:) * x xs), Typeable * (HList ((:) * x xs))) => Data (HList ((:) * x xs)) | |
Typeable * (HList ([] *)) => Data (HList ([] *)) | |
(Ord x, Ord (HList xs)) => Ord (HList ((:) * x xs)) | |
Ord (HList ([] *)) | |
(HProxies l, Read e, HSequence ReadP ((:) * (ReadP e) readP_l) ((:) * e l), HMapCxt HList ReadElement (AddProxy [*] l) readP_l) => Read (HList ((:) * e l)) | |
Read (HList ([] *)) | |
(Show e, Show (HList l)) => Show (HList ((:) * e l)) | |
Show (HList ([] *)) | |
(Ix x, Ix (HList xs)) => Ix (HList ((:) * x xs)) | |
Ix (HList ([] *)) | |
(HProxies a, HMapCxt HList ConstMempty (AddProxy [*] a) a, HZip HList a a aa, HMapCxt HList UncurryMappend aa a) => Monoid (HList a) | Analogous to the Monoid instance for tuples
|
(TypeRepsList (HList xs), Typeable * x) => TypeRepsList (HList ((:) * x xs)) | |
TypeRepsList (HList ([] *)) | |
HProject (HList l) (HList ([] *)) | |
HAppendList l1 l2 => HAppend (HList l1) (HList l2) | |
ApplyAB f e e' => ApplyAB (MapCar f) (e, HList l) (HList ((:) * e' l)) | |
HInits1 ([] *) ((:) * (HList ([] *)) ([] *)) | |
HTails ([] *) ((:) * (HList ([] *)) ([] *)) | |
Typeable ([*] -> *) HList | |
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) | |
(HConcatFD as bs, HAppendFD a bs cs) => HConcatFD ((:) * (HList a) as) cs | |
(HInits1 xs ys, HMapCxt HList (FHCons2 x) ys ys', (~) [*] (HMapCons x ys) ys', (~) [*] (HMapTail ys') ys) => HInits1 ((:) * x xs) ((:) * (HList ((:) * x ([] *))) ys') | |
HTails xs ys => HTails ((:) * x xs) ((:) * (HList ((:) * x xs)) ys) | |
HMapUnboxF xs us => HMapUnboxF ((:) * (HList x) xs) ((:) * (RecordU x) us) | |
((~) * (HList ((:) * x y)) z, HZip3 xs ys zs) => HZip3 ((:) * x xs) ((:) * (HList y) ys) ((:) * z zs) | |
type HExtendR e (HList l) = HList ((:) * e l) | |
type HAppendR * (HList l1) (HList l2) = HList (HAppendListR * l1 l2) | |
type HMapCons x ((:) * (HList a) b) = (:) * (HList ((:) * x a)) (HMapCons x b) | |
type UnHList (HList a) = a | |
data HList ([] *) = HNil | |
type ApplyR (Proxy Bool False, FHUProj sel ns) (HList ((:) * e l), Proxy HNat n) = ApplyR (FHUProj sel ns) (HList l, Proxy HNat (HSucc n)) | |
type ApplyR (Proxy Bool True, FHUProj sel ns) (HList ((:) * e l), Proxy HNat n) = HJust (e, (HList l, Proxy HNat (HSucc n))) | |
type ApplyR (FHUProj sel ns) (HList ((:) * e l), Proxy HNat n) = ApplyR (Proxy Bool (HBoolEQ sel (KMember n ns)), FHUProj sel ns) (HList ((:) * e l), Proxy HNat n) | |
type ApplyR (FHUProj sel ns) (HList ([] *), n) = HNothing | |
type HMapTail ((:) * (HList ((:) * a as)) bs) = (:) * (HList as) (HMapTail bs) | |
data HList ((:) * x xs) = x `HCons` (HList xs) |
class HProxiesFD xs pxs | pxs -> xs, xs -> pxs where Source
creates a HList of Proxies
HProxiesFD ([] *) ([] *) | |
HProxiesFD xs pxs => HProxiesFD ((:) * x xs) ((:) * (Proxy * x) pxs) |
type HProxies xs = HProxiesFD xs (AddProxy xs) Source
type family AddProxy xs :: k Source
Add Proxy
to a type
>>>
let x = undefined :: HList (AddProxy [Char,Int])
>>>
:t x
x :: HList '[Proxy Char, Proxy Int]
Basic list functions
Append
type family HAppendListR l1 l2 :: [k] Source
type HAppendListR k ([] k) l = l | |
type HAppendListR k ((:) k e l) l' = (:) k e (HAppendListR k l l') |
class HAppendList l1 l2 where Source
hAppendList :: HList l1 -> HList l2 -> HList (HAppendListR l1 l2) Source
the same as hAppend
HAppendList ([] *) l2 | |
HAppendList l l' => HAppendList ((:) * x l) l' |
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
hReverse_ :: HRevApp l1 ([] *) l3 => HList l1 -> HList l3 Source
a version of hReverse
that does not allow the type
information to flow backwards
A nicer notation for lists
hEnd :: HList l -> HList l Source
Note:
x :: HList a
- means:
forall a. x :: HList a
hEnd x
- means:
exists a. x :: HList a
List termination
class HBuild' l r where Source
HReverse l l' => HBuild' l (HList l') | |
(HReverse l lRev, HMapTaggedFn lRev l') => HBuild' l (Record l') | This instance allows creating Record with hBuild 3 |
HBuild' ((:) * a l) r => HBuild' l (a -> r) | |
((~) [*] (HRevAppR * l ([] *)) lRev, (~) * (HExtendRs * lRev (Proxy [*] ([] *))) (Proxy k l1), (~) k l' l1) => HBuild' l (Proxy k l') | see |
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"]
hBuild can also produce a Record, such that
hBuild x y ^. from unlabeled
can also be produced using
hEndR
$ hBuild x y
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.
foldl
class HFoldl f z xs r where Source
like foldl
>>>
hFoldl (uncurry $ flip (:)) [] (1 `HCons` 2 `HCons` HNil)
[2,1]
unfolds
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 HLengthEq es n => HReplicateFD n e es | n e -> es, es -> n where Source
Sometimes the result type can fix the type of the first argument:
>>>
hReplicate Proxy () :: HList '[ (), (), () ]
H[(),(),()]
However, with HReplicate all elements must have the same type, so it may be
easier to use HList2List
:
>>>
list2HList (repeat 3) :: Maybe (HList [Int, Int, Int])
Just H[3,3,3]
hReplicate :: Proxy n -> e -> HList es Source
HReplicateFD HZero e ([] *) | |
(HReplicateFD n e es, (~) * e e') => HReplicateFD (HSucc n) e ((:) * e' es) |
type HReplicate n e = HReplicateFD n e (HReplicateR n e) Source
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
type HReplicateR k HZero e = [] k | |
type HReplicateR k (HSucc n) e = (:) k e (HReplicateR k n e) |
class HLengthEq r n => HReplicateF n f z r | r -> n where Source
HReplicate produces lists that can be converted to ordinary lists
>>>
let two = hSucc (hSucc hZero)
>>>
let f = Fun' fromInteger :: Fun' Num Integer
>>>
:t applyAB f
applyAB f :: Num b => Integer -> b
>>>
hReplicateF two f 3
H[3,3]
>>>
hReplicateF Proxy f 3 :: HList [Int, Double, Integer]
H[3,3.0,3]
hReplicateF :: HLengthEq r n => Proxy n -> f -> z -> HList r Source
HReplicateF HZero f z ([] *) | |
(ApplyAB f z fz, HReplicateF n f z r') => HReplicateF (HSucc n) f z ((:) * fz r') |
iterate
class HLengthEq r n => HIterate n f z r where Source
This function behaves like iterate
, with an extra
argument to help figure out the result length
>>>
let three = hSucc (hSucc (hSucc hZero))
>>>
let f = Fun Just :: Fun '() Maybe
>>>
:t applyAB f
applyAB f :: a -> Maybe a
f is applied to different types:
>>>
hIterate three f ()
H[(),Just (),Just (Just ())]
It is also possible to specify the length later on,
as done with Prelude.iterate
>>>
let take3 x | _ <- hLength x `asTypeOf` three = x
>>>
take3 $ hIterate Proxy f ()
H[(),Just (),Just (Just ())]
concat
type HConcat xs = HConcatFD xs (HConcatR xs) Source
Like concat
but for HLists of HLists.
Works in ghci... puzzling as what is different in doctest (it isn't
-XExtendedDefaultRules
)
>>>
let a = hEnd $ hBuild 1 2 3
>>>
let b = hEnd $ hBuild 'a' "abc"
>>>
hConcat $ hBuild a b
H[1, 2, 3, 'a', "abc"]
type family HConcatR a :: [*] Source
type HConcatR ([] *) = [] * | |
type HConcatR ((:) * x xs) = HAppendListR * (UnHList x) (HConcatR xs) |
traversing HLists
producing HList
map
It could be implemented with hFoldr
, as we show further below
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.
>>>
: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 case)
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 ..., Read ...) => HList '[String, String] -> HList '[..., ...]
>>>
:t rl
rl :: (Read ..., Read ...) => HList '[String, String] -> HList '[..., ...]
>>>
:t ls
ls :: (Show ..., Show ...) => HList '[..., ...] -> HList '[String, String]
>>>
:t sl
sl :: (Show ..., Show ...) => HList '[..., ...] -> HList '[String, String]
HMap f |
hMap :: (HMapAux r f a b, SameLength' * * b a, SameLength' * * a b) => f -> r a -> r b Source
hMapL :: (HMapAux HList f a b, SameLength' * * b a, SameLength' * * a b) => f -> HList a -> HList b Source
hMap constrained to HList
HMapL f |
class (SameLength a b, HMapAux r f a b) => HMapCxt r f a b Source
(SameLength * * a b, HMapAux r f a b) => HMapCxt r f a b |
class HMapAux r f x y where Source
hMapAux :: SameLength x y => f -> r x -> r y Source
HMapAux HList (HFmap f) x y => HMapAux Record f x y | |
HMapAux Variant f xs ys => HMapAux TIC f xs ys | |
(ApplyAB f (GetElemTy x) (GetElemTy y), IArray UArray (GetElemTy y), IArray UArray (GetElemTy x)) => HMapAux RecordU f x y | |
HMapAux HList f ([] *) ([] *) | |
(ApplyAB f e e', HMapAux HList f l l', SameLength * * l l') => HMapAux HList f ((:) * e l) ((:) * e' l') | |
(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' ([] *)) |
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 -> a Source
>>>
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 where Source
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) |
alternative implementation
hSequence2 :: (HFoldr (LiftA2 FHCons) (f (HList ([] *))) l (f a), Applicative f) => HList l -> f a Source
hSequence2
is not recommended over hSequence
since it possibly doesn't
allow inferring argument types from the result types. Otherwise this version
should do exactly the same thing.
The DataKinds version needs a little help to find the type of the return HNil, unlike the original version, which worked just fine as
hSequence l = hFoldr ConsM (return HNil) l
producing homogenous lists
map (no sequencing)
This one we implement via hFoldr
Mapcar f |
hMapOut :: forall f e l. HMapOut f l e => f -> HList l -> [e] Source
compare hMapOut f
with hList2List
. hMap
f
mapM
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:
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 -> b Source
Check to see if an HList contains an element with a given type This is a type-level only test
type family HMemberP pred e1 l :: Bool Source
The following is a similar type-only membership test It uses the user-supplied curried type equality predicate pred
Another type-level membership test
class HMemberM e1 l r | e1 l -> r Source
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 where Source
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 where Source
HTIntersect t l1 l2 => HTIntersectBool False h t l1 l2 | |
HTIntersect t l1 l2 => HTIntersectBool True h t l1 ((:) * h l2) |
Convert between heterogeneous lists and homogeneous ones
class HList2List l e | l -> e where Source
hMapOut id
is similar, except this function is restricted
to HLists that actually contain a value (so the list produced
will be nonempty). This restriction allows adding a functional
dependency, which means that less type annotations can be necessary.
hList2List :: HList l -> [e] Source
list2HListSuffix :: [e] -> Maybe (HList l, [e]) Source
HList2List ((:) * e' l) e => HList2List ((:) * e ((:) * e' l)) e | |
HList2List ((:) * e ([] *)) e |
list2HList :: HList2List l e => [e] -> Maybe (HList l) Source
listAsHList :: (HList2List l1 e1, HList2List l e, Choice p, Applicative f) => p (HList l1) (f (HList l)) -> p [e1] (f [e]) Source
Prism [s] [t] (HList s) (HList t)
listAsHList' :: (HList2List l e, Choice p, Applicative f) => p (HList l) (f (HList l)) -> p [e] (f [e]) Source
Prism' [a] (HList s)
where s ~ HReplicateR n a
With HMaybe
Turn list in a list of justs
class (FromHJustR (ToHJustR l) ~ l) => ToHJust l where Source
the same as map Just
>>>
toHJust (2 .*. 'a' .*. HNil)
H[HJust 2,HJust 'a']
>>>
toHJust2 (2 .*. 'a' .*. HNil)
H[HJust 2,HJust 'a']
toHJust2 :: (HMapCxt r (HJust ()) a b, ToHJust a, b ~ ToHJustR a) => r a -> r b Source
alternative implementation. The Apply instance is in Data.HList.FakePrelude. A longer type could be inferred.
Extract justs from list of maybes
class (FromHJustR (ToHJustR l) ~ l) => FromHJust l where Source
type FromHJustR l :: [*] Source
fromHJust :: HList l -> HList (FromHJustR l) Source
alternative implementation
fromHJust2 :: HMapCxt r HFromJust a b => r a -> r b Source
This implementation is shorter.
Annotated lists
hAddTag :: (HMapAux r (HAddTag t) a b, SameLength' * * b a, SameLength' * * a b) => t -> r a -> r b Source
hRmTag :: (HMapAux r HRmTag a b, SameLength' * * b a, SameLength' * * a b) => r a -> r b Source
hFlag :: (HMapAux r (HAddTag (Proxy Bool True)) a b, SameLength' * * b a, SameLength' * * a b) => r a -> r b Source
Annotate list with a type-level Boolean
hFlag :: HMapCxt (HAddTag (Proxy True)) l r => HList l -> HList r
Splitting by HTrue and HFalse
Analogus to Data.List.partition
snd
. See also HPartition
>>>
let (.=.) :: p x -> y -> Tagged x y; _ .=. y = Tagged y
>>>
hSplit $ hTrue .=. 2 .*. hTrue .=. 3 .*. hFalse .=. 1 .*. HNil
(H[2,3],H[1])
it might make more sense to instead have LVPair Bool e
instead of (e, Proxy Bool)
since the former has the same
runtime representation as e
Splitting by Length
class (HLengthEq xs n, HAppendList1 xs ys xsys) => HSplitAt n xsys xs ys | n xsys -> xs ys, xs ys -> xsys, xs -> n where Source
setup
>>>
let two = hSucc (hSucc hZero)
>>>
let xsys = hEnd $ hBuild 1 2 3 4
If a length is explicitly provided, the resulting lists are inferred
>>>
hSplitAt two xsys
(H[1,2],H[3,4])
>>>
let sameLength_ :: SameLength a b => r a -> r b -> r a; sameLength_ = const
>>>
let len2 x = x `sameLength_` HCons () (HCons () HNil)
If the first chunk of the list (a) has to be a certain length, the type of the Proxy argument can be inferred.
>>>
case hSplitAt Proxy xsys of (a,b) -> (len2 a, b)
(H[1,2],H[3,4])
(HSplitAt1 ([] *) n xsys xs ys, HAppendList1 * xs ys xsys, HLengthEq xs n) => HSplitAt n xsys xs ys |
class (SameLength' (HReplicateR n ()) xs, HLengthEq1 xs n, HLengthEq2 xs n) => HLengthEq xs n | xs -> n Source
a better way to write HLength xs ~ n
because:
- it works properly with ghc-7.10 (probably another example of ghc bug #10009)
- it works backwards a bit in that if
n
is known, thenxs
can be refined:
>>>
undefined :: HLengthEq xs HZero => HList xs
H[]
(SameLength' * * (HReplicateR * n ()) xs, HLengthEq1 HNat xs n, HLengthEq2 HNat xs n) => HLengthEq xs n |
class HLengthEq1 xs n Source
(~) [*] xxs ([] *) => HLengthEq1 HNat xxs HZero | |
(HLengthEq xs n, (~) [*] xxs ((:) * x xs)) => HLengthEq1 HNat xxs (HSucc n) |
class HLengthEq2 xs n | xs -> n Source
(~) HNat zero HZero => HLengthEq2 HNat ([] *) zero | |
(HLengthEq xs n, (~) HNat sn (HSucc n)) => HLengthEq2 HNat ((:) * x xs) sn |
class HStripPrefix xs xsys ys => HAppendList1 xs ys xsys | xs ys -> xsys, xs xsys -> ys Source
HAppendList1 xs ys xsys
is the type-level way of saying xs ++ ys == xsys
used by HSplitAt
HAppendList1 k ([] k) ys ys | |
HAppendList1 k xs ys zs => HAppendList1 k ((:) k x xs) ys ((:) k x zs) |
class HStripPrefix xs xsys ys | xs xsys -> ys Source
analog of stripPrefix
HStripPrefix [k] k k ([] k) ys ys | |
((~) k1 x' x, HStripPrefix [k1] [k1] k xs xsys ys) => HStripPrefix [k] [k] k ((:) k x' xs) ((:) k x xsys) ys |
Conversion to and from tuples
class HTuple v t | v -> t, t -> v where Source
HTuple ([] *) () | |
HTuple ((:) * a ((:) * b ([] *))) (a, b) | |
HTuple ((:) * a ((:) * b ((:) * c ([] *)))) (a, b, c) | |
HTuple ((:) * a ((:) * b ((:) * c ((:) * d ([] *))))) (a, b, c, d) | |
HTuple ((:) * a ((:) * b ((:) * c ((:) * d ((:) * e ([] *)))))) (a, b, c, d, e) | |
HTuple ((:) * a ((:) * b ((:) * c ((:) * d ((:) * e ((:) * f ([] *))))))) (a, b, c, d, e, f) |
hTuple :: (HTuple v1 b, HTuple v a, Profunctor p, Functor f) => p a (f b) -> p (HList v) (f (HList v1)) Source
Iso (HList v) (HList v') a b
hTuple' :: (HTuple v b, Profunctor p, Functor f) => p b (f b) -> p (HList v) (f (HList v)) Source
Iso' (HList v) a
partition
class HPartitionEq f x1 xs xi xo | f x1 xs -> xi xo where Source
HPartitionEq f x1 xs xi xo
is analogous to
(xi,xo) = partition (f x1) xs
where f
is a "function" passed in using it's instance of HEqBy
HPartitionEq k k f x1 ([] *) ([] *) ([] *) | |
(HEqBy k * f x1 x b, HPartitionEq1 k * b f x1 x xs xi xo) => HPartitionEq k * f x1 ((:) * x xs) xi xo |
class HPartitionEq1 b f x1 x xs xi xo | b f x1 x xs -> xi xo where Source
HPartitionEq k k1 f x1 xs xi xo => HPartitionEq1 k k False f x1 x xs xi ((:) * x xo) | |
HPartitionEq k k1 f x1 xs xi xo => HPartitionEq1 k k True f x1 x xs ((:) * x xi) xo |
groupBy
span
class HSpanEqBy f x y fst snd | f x y -> fst snd, fst snd -> y where Source
HSpanEq x y fst snd
is analogous to (fst,snd) =
span
(== x) y
(HSpanEqBy1 k f x y fst snd, (~) [*] (HAppendListR * fst snd) y) => HSpanEqBy k f x y fst snd |
class HSpanEqBy1 f x y i o | f x y -> i o where Source
HSpanEqBy1 k f x ([] *) ([] *) ([] *) | |
(HEqBy k * f x y b, HSpanEqBy2 k b f x y ys i o) => HSpanEqBy1 k f x ((:) * y ys) i o |
class HSpanEqBy2 b f x y ys i o | b f x y ys -> i o where Source
HSpanEqBy2 k False f x y ys ([] *) ((:) * y ys) | |
HSpanEqBy1 k f x zs i o => HSpanEqBy2 k True f x y zs ((:) * y i) o |
zip
see alternative implementations in Data.HList.HZip