monad-effect-0.2.3.0: src/Data/TypeList.hs
{-# LANGUAGE UndecidableSuperClasses, DefaultSignatures, AllowAmbiguousTypes, UndecidableInstances, DataKinds, TypeFamilies #-}
-- | This module provides utilities for working with type-level lists in Haskell.
-- It defines various types and functions to manipulate type-level lists, including
-- finite lists, constrained lists, and dynamic lists.
--
-- This module is considered INTERNAL, you can use it but be aware that the API may change without major version bumps.
module Data.TypeList
( module Data.TypeList.FList
, module Data.TypeList.UList
, module Data.TypeList.Families
, module Data.TypeList.ConsFData
, module Data.TypeList
, module Data.Result
) where
import Data.TypeList.Families
import Data.TypeList.FList
import Data.TypeList.UList
import Data.TypeList.ConsFData
import Data.TypeList.ConsFData.Pattern
import Data.Type.Equality
import Data.Result
import Data.Kind
import Unsafe.Coerce
-- | A class carrying the proof of the existence of an element in a list.
class InList (e :: Type) (es :: [Type]) where
singIndex :: SNat (FirstIndex e es)
singFirstIndex :: SFirstIndex e es
proofIndex :: AtIndex es (FirstIndex e es) :~: e
elemIndex :: Elem e es
getEMaybe :: EList es -> Maybe e
getEMaybe = getEMaybeS (singFirstIndex @e @es)
{-# INLINE getEMaybe #-}
embedE :: e -> EList es
embedE = embedES (singFirstIndex @e @es)
{-# INLINE embedE #-}
class InList e es => In' flist e es where
getIn :: flist f es -> f e
default getIn :: (ConsFData flist) => flist f es -> f e
getIn = getInS (singFirstIndex @e @es)
{-# INLINE getIn #-}
modifyIn :: (f e -> f e) -> flist f es -> flist f es
default modifyIn :: (ConsFData flist) => (f e -> f e) -> flist f es -> flist f es
modifyIn = modifyInS (singFirstIndex @e @es)
{-# INLINE modifyIn #-}
lensIn :: forall f fun. Functor fun => (f e -> fun (f e)) -> flist f es -> fun (flist f es)
default lensIn :: (ConsFData flist) => forall fun. Functor fun => (f e -> fun (f e)) -> flist f es -> fun (flist f es)
lensIn = lensInS (singFirstIndex @e @es)
{-# INLINE lensIn #-}
-- | Axiom
firstIndexTraverseNotEqElem :: forall e t ts. (NotEq e t, InList e ts) => FirstIndex e (t : ts) :~: Succ (FirstIndex e ts)
firstIndexTraverseNotEqElem = unsafeCoerce Refl
{-# INLINE firstIndexTraverseNotEqElem #-}
-- | Axiom
axiomInImpliesNonEmpty :: InList e ts => NonEmpty ts :~: True
axiomInImpliesNonEmpty = unsafeCoerce Refl
{-# INLINE axiomInImpliesNonEmpty #-}
-- | Base case for the InList class. NotIn e ts =>
instance InList e (e : ts) where
singIndex = SZero
{-# INLINE singIndex #-}
singFirstIndex = SFirstIndexZero
{-# INLINE singFirstIndex #-}
proofIndex = Refl
{-# INLINE proofIndex #-}
elemIndex = EZ
{-# INLINE elemIndex #-}
embedE = EHead
{-# INLINE embedE #-}
getEMaybe = \case
EHead x -> Just x
ETail _ -> Nothing
{-# INLINE getEMaybe #-}
instance In' FList e (e : ts) where
getIn = \(e :** _) -> e
{-# INLINE getIn #-}
modifyIn f = \(x :** xs) -> f x :** xs
{-# INLINE modifyIn #-}
-- | InListductive case for the In class. UniqueIn e (t : ts),
instance {-# OVERLAPPABLE #-} (NotEq e t, InList e ts) => InList e (t : ts) where
singIndex = case firstIndexTraverseNotEqElem @e @t @ts of
Refl -> SSucc (singIndex @e @ts)
{-# INLINE singIndex #-}
singFirstIndex = case firstIndexTraverseNotEqElem @e @t @ts of
Refl -> case axiomInImpliesNonEmpty @e @ts of
Refl -> SFirstIndexSucc Refl (singFirstIndex @e @ts)
{-# INLINE singFirstIndex #-}
proofIndex = case firstIndexTraverseNotEqElem @e @t @ts of
Refl -> case proofIndex @e @ts of Refl -> Refl
{-# INLINE proofIndex #-}
elemIndex = ES elemIndex
{-# INLINE elemIndex #-}
embedE = ETail . embedE
{-# INLINE embedE #-}
getEMaybe = \case
EHead _ -> Nothing
ETail es -> getEMaybe es
{-# INLINE getEMaybe #-}
instance {-# OVERLAPPABLE #-} (flist ~ FList, NonEmpty ts ~ True, FDataConstraint flist e (t : ts), ConsFDataList flist (t:ts), NotEq e t, In' flist e ts) => In' FList e (t : ts) where
getIn = \(_ :*** xs) -> getIn xs
{-# INLINE getIn #-}
modifyIn f = \(x :*** xs) -> x :*** modifyIn f xs
{-# INLINE modifyIn #-}
instance {-# INCOHERENT #-} InList e es => In' FList e es
class SubList (flist :: (Type -> Type) -> [Type] -> Type) (ys :: [Type]) (xs :: [Type]) where
getSubListF :: flist f xs -> flist f ys -- ^ Get the sublist from the FList.
subListModifyF :: (flist f ys -> flist f ys) -> flist f xs -> flist f xs -- ^ Modify the sublist in the FList.
class SubListEmbed (ys :: [Type]) (xs :: [Type]) where
subListResultEmbed :: Result ys a -> Result xs a -- ^ Embed the result of a sublist operation.
type NonEmptySubList ys xs = (NonEmpty ys ~ True, NonEmptySubList' ys xs)
subListEListEmbed :: forall ys xs. NonEmptySubList ys xs => EList ys -> EList xs
subListEListEmbed = subListEListEmbed' @ys @xs
{-# INLINE subListEListEmbed #-}
class NonEmptySubList' (ys :: [Type]) (xs :: [Type]) where
subListEListEmbed' :: EList ys -> EList xs
instance NonEmptySubList' '[] xs where
subListEListEmbed' = \case {}
{-# INLINE subListEListEmbed' #-}
instance (InList y xs, NonEmptySubList' ys xs) => NonEmptySubList' (y:ys) xs where
subListEListEmbed' (EHead y) = embedE y
subListEListEmbed' (ETail ys) = subListEListEmbed' ys
{-# INLINE subListEListEmbed' #-}
subListUpdateF :: (SubList flist ys xs) => flist f xs -> flist f ys -> flist f xs
subListUpdateF xs ys = subListModifyF (const ys) xs
{-# INLINE subListUpdateF #-}
instance ConsFNil c => SubList c '[] xs where
getSubListF _ = fNil
{-# INLINE getSubListF #-}
subListModifyF _ xs = xs
{-# INLINE subListModifyF #-}
instance SubListEmbed '[] xs where
subListResultEmbed (RSuccess a) = RSuccess a
{-# INLINE subListResultEmbed #-}
instance (InList y xs, SubListEmbed ys xs) => SubListEmbed (y:ys) xs where
subListResultEmbed (RSuccess a) = RSuccess a
subListResultEmbed (RFailure (EHead y)) = RFailure (embedE y)
subListResultEmbed (RFailure (ETail ys)) = subListResultEmbed (RFailure ys)
{-# INLINE subListResultEmbed #-}
instance {-# INCOHERENT #-} SubList c xs xs where
getSubListF = id
{-# INLINE getSubListF #-}
subListModifyF = id
{-# INLINE subListModifyF #-}
instance {-# INCOHERENT #-} SubListEmbed xs xs where
subListResultEmbed = id
{-# INLINE subListResultEmbed #-}
-- | Induction case for the SubList class.
instance (In' c y xs, ConsFDataList c (y:ys), SubList c ys xs) => SubList c (y : ys) xs where
getSubListF xs = consF0 (getIn xs) (getSubListF xs)
{-# INLINE getSubListF #-}
subListModifyF f xs =
let hy :*** hys = f (getSubListF xs)
in modifyIn (const hy) $ subListUpdateF xs hys
{-# INLINE subListModifyF #-}
instance {-# INCOHERENT #-} ConsFDataList c (x:xs) => SubList c xs (x:xs) where
getSubListF (_ :*** xs) = xs
{-# INLINE getSubListF #-}
subListModifyF f (x :*** xs) = x :*** f xs
{-# INLINE subListModifyF #-}