{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE UndecidableInstances #-} module Polysemy.ConstraintAbsorber.MonadState ( absorbState ) where import Polysemy import Polysemy.ConstraintAbsorber import Polysemy.State import qualified Control.Monad.State.Class as S ------------------------------------------------------------------------------ -- | Introduce a local 'S.MonadState' constraint on 'Sem' --- allowing it to -- interop nicely with MTL. -- -- @since 0.3.0.0 absorbState :: Member (State s) r => (S.MonadState s (Sem r) => Sem r a) -- ^ A computation that requires an instance of 'S.MonadState' for -- 'Sem'. This might be something with type @'S.MonadState' s m => m a@. -> Sem r a absorbState = absorbWithSem @(S.MonadState _) @Action (StateDict get put) (Sub Dict) {-# INLINEABLE absorbState #-} ------------------------------------------------------------------------------ -- | A Dictionary of the functions we need to supply -- to make an instance of State data StateDict s m = StateDict { get_ :: m s , put_ :: s -> m () } ------------------------------------------------------------------------------ -- | Wrapper for a monadic action with phantom type parameter for reflection. -- Locally defined so that the instance we are going to build with reflection -- must be coherent, that is there cannot be orphans. newtype Action m s' a = Action (m a) deriving (Functor, Applicative, Monad) ------------------------------------------------------------------------------ -- | Given a reifiable mtl State dictionary, -- we can make an instance of @MonadState@ for the action -- wrapped in @Action@. instance ( Monad m , Reifies s' (StateDict s m) ) => S.MonadState s (Action m s') where get = Action $ get_ $ reflect $ Proxy @s' {-# INLINEABLE get #-} put s = Action $ put_ (reflect $ Proxy @s') s {-# INLINEABLE put #-}