module Sqel.Class.Mods where import Generics.SOP (I (I), NP (Nil, (:*))) import Prelude hiding (Compose) import Sqel.Data.Dd (Dd (Dd), DdK (DdK), (:>) ((:>))) import Sqel.Data.Mods (Mods (Mods), unMods) class SymNP p ps where symNP :: p -> NP I ps instance {-# overlappable #-} ( ps ~ '[p] ) => SymNP p ps where symNP p = I p :* Nil instance ( SymNP p1 ps ) => SymNP (p0 :> p1) (p0 : ps) where symNP (p0 :> p1) = I p0 :* symNP p1 instance SymNP (NP I ps) ps where symNP = id symMods :: SymNP p ps => p -> Mods ps symMods p = Mods (symNP p) class MapMod' p ps0 ps1 | p ps0 -> ps1 where mapMod' :: p -> (p -> p) -> Mods ps0 -> Mods ps1 instance MapMod' p (p : ps) (p : ps) where mapMod' _ f (Mods (I p :* ps)) = Mods (I (f p) :* ps) instance {-# overlappable #-} ( MapMod' p ps0 ps1 ) => MapMod' p (a' : ps0) (a' : ps1) where mapMod' p f (Mods (a' :* ps)) = Mods (a' :* unMods (mapMod' p f (Mods ps))) instance MapMod' p '[] '[p] where mapMod' p f (Mods Nil) = Mods (I (f p) :* Nil) amendMod' :: MapMod' p ps0 ps1 => p -> Mods ps0 -> Mods ps1 amendMod' p = mapMod' p id setMod' :: MapMod' p ps0 ps1 => p -> Mods ps0 -> Mods ps1 setMod' p = mapMod' p (const p) -- TODO this could map over multiple matching mods class OverMod' p ps where overMod' :: (p -> p) -> Mods ps -> Mods ps instance OverMod' p (p : ps) where overMod' f (Mods (I p :* ps)) = Mods (I (f p) :* ps) instance ( OverMod' p ps ) => OverMod' p (a' : ps) where overMod' f (Mods (a' :* ps)) = Mods (a' :* unMods (overMod' f (Mods ps))) instance OverMod' p '[] where overMod' _ (Mods Nil) = Mods Nil class CMapMod' c p0 p p1 ps0 ps1 | ps0 p0 p1 -> p ps1 where cmapMod' :: p0 -> (c p p1 => p -> p1) -> Mods ps0 -> Mods ps1 instance ( c p p1 ) => CMapMod' c p0 p p1 (p : ps) (p1 : ps) where cmapMod' _ f (Mods (I p :* ps)) = Mods (I (f p) :* ps) instance ( CMapMod' c p0 p p1 ps0 ps1 ) => CMapMod' c p0 p p1 (a' : ps0) (a' : ps1) where cmapMod' p f (Mods (a' :* ps)) = Mods (a' :* unMods (cmapMod' @c @p0 @p @p1 @ps0 @ps1 p f (Mods ps))) instance CMapMod' c p0 p1 p1 '[] '[p0] where cmapMod' p _ (Mods Nil) = Mods (I p :* Nil) type GetMod :: Constraint -> Type -> [Type] -> Constraint class GetMod c p ps where getMod :: (c => p) -> Mods ps -> p instance c => GetMod c p '[] where getMod f (Mods Nil) = f instance GetMod c p (p : ps) where getMod _ (Mods (I p :* _)) = p class AddMod p s0 s1 | p s0 -> s1 where addMod :: p -> Dd s0 -> Dd s1 instance AddMod p ('DdK sel ps a s) ('DdK sel (p : ps) a s) where addMod p (Dd sel (Mods ps) s) = Dd sel (Mods (I p :* ps)) s instance {-# overlappable #-} ( GetMod c p ps ) => GetMod c p (a' : ps) where getMod f (Mods (_ :* ps)) = getMod @c f (Mods ps) class MapMod p s0 s1 | p s0 -> s1 where mapMod :: p -> (p -> p) -> Dd s0 -> Dd s1 instance ( MapMod' p ps0 ps1 ) => MapMod p ('DdK sel ps0 a s0) ('DdK sel ps1 a s0) where mapMod p f (Dd sel ps0 s) = Dd sel (mapMod' p f ps0) s class OverMod p s where overMod :: (p -> p) -> Dd s -> Dd s instance ( OverMod' p ps ) => OverMod p ('DdK sel ps a s0) where overMod f (Dd sel ps s) = Dd sel (overMod' f ps) s class CMapMod c p0 p p1 s0 s1 | s0 p0 p1 -> p s1 where cmapMod :: p0 -> (c p p1 => p -> p1) -> Dd s0 -> Dd s1 instance ( CMapMod' c p0 p p1 ps0 ps1 ) => CMapMod c p0 p p1 ('DdK sel ps0 a s0) ('DdK sel ps1 a s0) where cmapMod p0 f (Dd sel ps0 s) = Dd sel (cmapMod' @c @_ @_ @p1 p0 f ps0) s -- TODO this appends the mod if it is missing, while it should prepend it to preserve the order of effects. amendMod :: MapMod p s0 s1 => p -> Dd s0 -> Dd s1 amendMod p = mapMod p id setMod :: MapMod p s0 s1 => p -> Dd s0 -> Dd s1 setMod p = mapMod p (const p) type OptMod :: Type -> [Type] -> Type -> Constraint class OptMod p ps res | ps p -> res where optMod :: Mods ps -> res instance OptMod p '[] () where optMod (Mods Nil) = () instance OptMod p (p : ps) p where optMod (Mods (I p :* _)) = p instance {-# overlappable #-} ( OptMod p ps p1 ) => OptMod p (p0 : ps) p1 where optMod (Mods (_ :* ps)) = optMod @p (Mods ps) type MaybeMod :: Type -> [Type] -> Constraint class MaybeMod p ps where maybeMod :: Mods ps -> Maybe p instance MaybeMod p '[] where maybeMod (Mods Nil) = Nothing instance MaybeMod p (p : ps) where maybeMod (Mods (I p :* _)) = Just p instance {-# overlappable #-} ( MaybeMod p ps ) => MaybeMod p (p0 : ps) where maybeMod (Mods (_ :* ps)) = maybeMod @p (Mods ps)