effectful-core-2.2.2.0: src/Effectful/Internal/Effect.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_HADDOCK not-home #-}
-- | Type-safe indexing for 'Effectful.Internal.Monad.Env'.
--
-- This module is intended for internal use only, and may change without warning
-- in subsequent releases.
module Effectful.Internal.Effect
( Effect
, (:>)(..)
, (:>>)
, Subset(..)
, KnownPrefix(..)
, IsUnknownSuffixOf
-- * Re-exports
, Type
) where
import Data.Kind
import GHC.TypeLits
-- | The kind of effects.
type Effect = (Type -> Type) -> Type -> Type
-- | A constraint that requires that a particular effect @e@ is a member of the
-- type-level list @es@. This is used to parameterize an 'Effectful.Eff'
-- computation over an arbitrary list of effects, so long as @e@ is /somewhere/
-- in the list.
--
-- For example, a computation that only needs access to a mutable value of type
-- 'Integer' would have the following type:
--
-- @
-- 'Effectful.State.Static.Local.State' 'Integer' ':>' es => 'Effectful.Eff' es ()
-- @
class (e :: Effect) :> (es :: [Effect]) where
-- | Get the position of @e@ in @es@.
--
-- /Note:/ GHC is kind enough to cache these values as they're top level CAFs,
-- so the lookup is amortized @O(1)@ without any language level tricks.
reifyIndex :: Int
reifyIndex =
-- Don't show "minimal complete definition" in haddock.
error "reifyIndex"
instance TypeError
( Text "There is no handler for '" :<>: ShowType e :<>: Text "' in the context"
) => e :> '[] where
reifyIndex = error "unreachable"
instance {-# OVERLAPPING #-} e :> (e : es) where
reifyIndex = 0
instance e :> es => e :> (x : es) where
reifyIndex = 1 + reifyIndex @e @es
----------------------------------------
-- | Convenience operator for expressing that a function uses multiple effects
-- in a more concise way than enumerating them all with '(:>)'.
--
-- @[E1, E2, ..., En] ':>>' es ≡ (E1 ':>' es, E2 ':>' es, ..., En :> es)@
type family xs :>> es :: Constraint where
'[] :>> es = ()
(x : xs) :>> es = (x :> es, xs :>> es)
{-# DEPRECATED (:>>) "Usage of (:>>) slows down GHC too much, so it will be removed in 3.0.0.0. See https://github.com/haskell-effectful/effectful/issues/52#issuecomment-1269155485 for more information." #-}
----------------------------------------
-- | Provide evidence that @xs@ is a subset of @es@.
class KnownPrefix es => Subset (xs :: [Effect]) (es :: [Effect]) where
subsetFullyKnown :: Bool
subsetFullyKnown =
-- Don't show "minimal complete definition" in haddock.
error "subsetFullyKnown"
reifyIndices :: [Int]
reifyIndices =
-- Don't show "minimal complete definition" in haddock.
error "reifyIndices"
-- If the subset is not fully known, make sure the subset and the base stack
-- have the same unknown suffix.
instance {-# INCOHERENT #-}
( KnownPrefix es
, xs `IsUnknownSuffixOf` es
) => Subset xs es where
subsetFullyKnown = False
reifyIndices = []
-- If the subset is fully known, we're done.
instance KnownPrefix es => Subset '[] es where
subsetFullyKnown = True
reifyIndices = []
instance (e :> es, Subset xs es) => Subset (e : xs) es where
subsetFullyKnown = subsetFullyKnown @xs @es
reifyIndices = reifyIndex @e @es : reifyIndices @xs @es
----
-- | Calculate length of a statically known prefix of @es@.
class KnownPrefix (es :: [Effect]) where
prefixLength :: Int
instance KnownPrefix es => KnownPrefix (e : es) where
prefixLength = 1 + prefixLength @es
instance {-# INCOHERENT #-} KnownPrefix es where
prefixLength = 0
----
-- | Require that @xs@ is the unknown suffix of @es@.
class (xs :: [Effect]) `IsUnknownSuffixOf` (es :: [Effect])
instance {-# INCOHERENT #-} xs ~ es => xs `IsUnknownSuffixOf` es
instance xs `IsUnknownSuffixOf` es => xs `IsUnknownSuffixOf` (e : es)