witness-0.5: src/Data/Witness/HList.hs
module Data.Witness.HList where
import Control.Applicative
import Control.Category
import Control.Category.Tensor
import Data.Constraint (Dict(..))
import Data.Functor.Identity as Import
import Data.Kind
import Data.Type.Equality
import Data.Witness.Either
import Data.Witness.List
import Data.Witness.ListElement
import Prelude hiding ((.), id)
import Unsafe.Coerce
type family HList (w :: [Type]) = (r :: Type) | r -> w where
HList '[] = ()
HList (t : tt) = (t, HList tt)
-- workaround for https://gitlab.haskell.org/ghc/ghc/issues/10833
injectiveHList ::
forall (a :: [Type]) (b :: [Type]). HList a ~ HList b
=> a :~: b
injectiveHList = unsafeCoerce Refl
hListEq :: (forall a. w a -> Dict (Eq a)) -> ListType w t -> Dict (Eq (HList t))
hListEq _ NilListType = Dict
hListEq f (ConsListType t tt) =
case (f t, hListEq f tt) of
(Dict, Dict) -> Dict
hListShow :: (forall a. w a -> Dict (Show a)) -> ListType w t -> Dict (Show (HList t))
hListShow _ NilListType = Dict
hListShow f (ConsListType t tt) =
case (f t, hListShow f tt) of
(Dict, Dict) -> Dict
data HListWit (wit :: Type -> Type) (t :: Type) where
MkHListWit :: forall (wit :: Type -> Type) (lt :: [Type]). ListType wit lt -> HListWit wit (HList lt)
listFill :: ListType w t -> (forall a. w a -> a) -> HList t
listFill NilListType _f = ()
listFill (ConsListType wa wr) f = (f wa, listFill wr f)
listMap :: ListType w t -> (forall a. w a -> a -> a) -> HList t -> HList t
listMap NilListType _f () = ()
listMap (ConsListType wa wr) f (a, rest) = (f wa a, listMap wr f rest)
listLift2 :: ListType w t -> (forall a. w a -> a -> a -> a) -> HList t -> HList t -> HList t
listLift2 NilListType _f () () = ()
listLift2 (ConsListType wa wr) f (a, resta) (b, restb) = (f wa a b, listLift2 wr f resta restb)
listIdentity :: ListType Identity lt -> HList lt
listIdentity NilListType = ()
listIdentity (ConsListType (Identity a) rest) = (a, listIdentity rest)
listSequence :: (Applicative f) => ListType f lt -> f (HList lt)
listSequence NilListType = pure ()
listSequence (ConsListType fa rest) = liftA2 (,) fa (listSequence rest)
getListElement :: ListElementType list t -> HList list -> t
getListElement FirstElementType = fst -- using fst and snd for irrefutable matching
getListElement (RestElementType lw) = getListElement lw . snd
putListElement :: ListElementType list t -> t -> HList list -> HList list
putListElement FirstElementType t (_, r) = (t, r)
putListElement (RestElementType lw) t (a, r) = (a, putListElement lw t r)
modifyListElement :: ListElementType list t -> (t -> t) -> HList list -> HList list
modifyListElement n aa t = putListElement n (aa (getListElement n t)) t
data AppendList w la lb = forall lr. MkAppendList
{ listAppendWitness :: ListType w lr
, listAppend :: HList la -> HList lb -> HList lr
, listSplit :: HList lr -> (HList la, HList lb)
}
appendList :: ListType w la -> ListType w lb -> AppendList w la lb
appendList NilListType wlb =
MkAppendList {listAppendWitness = wlb, listAppend = \() lb -> lb, listSplit = \lb -> ((), lb)}
appendList (ConsListType wa wla) wlb =
case appendList wla wlb of
MkAppendList wit join split ->
MkAppendList
{ listAppendWitness = ConsListType wa wit
, listAppend = \(a, la) lb -> (a, join la lb)
, listSplit =
\(a, lab) ->
case split lab of
(la, lb) -> ((a, la), lb)
}
data AddItemList w a l = forall lr. MkAddItemList
{ listAddItemWitness :: ListType w lr
, listAddItem :: a -> HList l -> HList lr
, listSplitItem :: HList lr -> (a, HList l)
}
addListItem :: w a -> ListType w l -> AddItemList w a l
addListItem wa wl = MkAddItemList {listAddItemWitness = ConsListType wa wl, listAddItem = (,), listSplitItem = id}
data MergeItemList w a l = forall lr. MkMergeItemList
{ listMergeItemWitness :: ListType w lr
, listMergeItem :: (Maybe a -> a) -> HList l -> HList lr
, listUnmergeItem :: HList lr -> (a, HList l)
}
mergeListItem :: (TestEquality w) => ListType w l -> w a -> MergeItemList w a l
mergeListItem NilListType wa =
MkMergeItemList
{ listMergeItemWitness = ConsListType wa NilListType
, listMergeItem = \maa () -> (maa Nothing, ())
, listUnmergeItem = id
}
mergeListItem wl@(ConsListType wa' _) wa
| Just Refl <- testEquality wa wa' =
MkMergeItemList
{ listMergeItemWitness = wl
, listMergeItem = \maa (a, l) -> (maa (Just a), l)
, listUnmergeItem = \(a, l) -> (a, (a, l))
}
mergeListItem (ConsListType wa' wl) wa =
case mergeListItem wl wa of
MkMergeItemList wit merge unmerge ->
MkMergeItemList
{ listMergeItemWitness = ConsListType wa' wit
, listMergeItem = \maa (a', l) -> (a', merge maa l)
, listUnmergeItem =
\(a', l') ->
case unmerge l' of
(a, l) -> (a, (a', l))
}
data MergeList w la lb = forall lr. MkMergeList
{ listMergeWitness :: ListType w lr
, listMerge :: (forall t. w t -> t -> t -> t) -> HList la -> HList lb -> HList lr
, listUnmerge :: HList lr -> (HList la, HList lb)
}
mergeList :: (TestEquality w) => ListType w la -> ListType w lb -> MergeList w la lb
mergeList wla NilListType =
MkMergeList {listMergeWitness = wla, listMerge = \_ la () -> la, listUnmerge = \la -> (la, ())}
mergeList wla (ConsListType wb wlb) =
case mergeListItem wla wb of
MkMergeItemList wla' mergeItem unmergeItem ->
case mergeList wla' wlb of
MkMergeList wlr merge unmerge ->
MkMergeList
{ listMergeWitness = wlr
, listMerge =
\f la (b, lb) ->
merge
f
(mergeItem
(\mb' ->
case mb' of
Just b' -> f wb b' b
Nothing -> b)
la)
lb
, listUnmerge =
\lr ->
case unmerge lr of
(la', lb) ->
case unmergeItem la' of
(b, la) -> (la, (b, lb))
}
type MapWitness cc w1 w2 = forall r v1. w1 v1 -> (forall v2. w2 v2 -> (cc v1 v2) -> r) -> r
sameMapWitness :: (forall v. w v -> cc v v) -> MapWitness cc w w
sameMapWitness wc w wcr = wcr w (wc w)
data MapList cc w2 l = forall lr. MkMapList
{ listMapWitness :: ListType w2 lr
, listMapW :: cc (HList l) (HList lr)
}
mapList :: (Tensor cc) => MapWitness cc w1 w2 -> ListType w1 l -> MapList cc w2 l
mapList _ NilListType = MkMapList {listMapWitness = NilListType, listMapW = tensorUnit}
mapList mapwit (ConsListType w rest) =
case mapList mapwit rest of
MkMapList wit listMapW' ->
mapwit
w
(\w' vmap -> MkMapList {listMapWitness = ConsListType w' wit, listMapW = tensorPair vmap listMapW'})
data RemoveFromList w a l = forall lr. MkRemoveFromList
{ listRemoveWitness :: ListType w lr
, listInsert :: a -> HList lr -> HList l
, listRemove :: HList l -> HList lr
}
removeAllMatching :: (TestEquality w) => w a -> ListType w l -> RemoveFromList w a l
removeAllMatching _ NilListType =
MkRemoveFromList {listRemoveWitness = NilListType, listInsert = \_ -> id, listRemove = id}
removeAllMatching wa (ConsListType wb rest) =
case removeAllMatching wa rest of
MkRemoveFromList wit ins rm ->
case testEquality wa wb of
Just Refl ->
MkRemoveFromList
{listRemoveWitness = wit, listInsert = \a l2 -> (a, ins a l2), listRemove = \(_, l1) -> rm l1}
Nothing ->
MkRemoveFromList
{ listRemoveWitness = ConsListType wb wit
, listInsert = \a (b, l2) -> (b, ins a l2)
, listRemove = \(b, l1) -> (b, rm l1)
}
data RemoveManyFromList wit lx l = forall lr. MkRemoveManyFromList
{ listRemoveManyWitness :: ListType wit lr
, listInsertMany :: HList lx -> HList lr -> HList l
, listRemoveMany :: HList l -> HList lr
}
removeAllMatchingMany :: (TestEquality wit) => ListType wit lx -> ListType wit l -> RemoveManyFromList wit lx l
removeAllMatchingMany NilListType wl =
MkRemoveManyFromList {listRemoveManyWitness = wl, listInsertMany = \_ lr -> lr, listRemoveMany = \l -> l}
removeAllMatchingMany (ConsListType wa wlx) wl =
case removeAllMatching wa wl of
MkRemoveFromList wl' ins rm ->
case removeAllMatchingMany wlx wl' of
MkRemoveManyFromList wl'' insM remM ->
MkRemoveManyFromList
{ listRemoveManyWitness = wl''
, listInsertMany = \(a, lx) lr -> ins a (insM lx lr)
, listRemoveMany = remM . rm
}
data PartitionList wit1 wit2 l = forall l1 l2. MkPartitionList
{ listPartitionWitness1 :: ListType wit1 l1
, listPartitionWitness2 :: ListType wit2 l2
, listFromPartition :: HList l1 -> HList l2 -> HList l
, listToPartition1 :: HList l -> HList l1
, listToPartition2 :: HList l -> HList l2
}
partitionList :: ListType (EitherType w1 w2) l -> PartitionList w1 w2 l
partitionList NilListType =
MkPartitionList
{ listPartitionWitness1 = NilListType
, listPartitionWitness2 = NilListType
, listFromPartition = \() () -> ()
, listToPartition1 = \() -> ()
, listToPartition2 = \() -> ()
}
partitionList (ConsListType (LeftType w1a) rest) =
case partitionList rest of
MkPartitionList pw1 pw2 fp tp1 tp2 ->
MkPartitionList
{ listPartitionWitness1 = ConsListType w1a pw1
, listPartitionWitness2 = pw2
, listFromPartition = \(a, l1) l2 -> (a, fp l1 l2)
, listToPartition1 = \(a, l) -> (a, tp1 l)
, listToPartition2 = \(_, l) -> tp2 l
}
partitionList (ConsListType (RightType w2a) rest) =
case partitionList rest of
MkPartitionList pw1 pw2 fp tp1 tp2 ->
MkPartitionList
{ listPartitionWitness1 = pw1
, listPartitionWitness2 = ConsListType w2a pw2
, listFromPartition = \l1 (a, l2) -> (a, fp l1 l2)
, listToPartition1 = \(_, l) -> tp1 l
, listToPartition2 = \(a, l) -> (a, tp2 l)
}