{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
module Data.Extensible.Effect (
Instruction(..)
, Eff
, liftEff
, liftsEff
, hoistEff
, castEff
, Interpreter(..)
, handleEff
, peelEff
, Rebinder
, rebindEff0
, peelEff0
, rebindEff1
, peelEff1
, rebindEff2
, leaveEff
, retractEff
, Action(..)
, Function
, runAction
, (@!?)
, peelAction
, peelAction0
, ReaderEff
, askEff
, asksEff
, localEff
, runReaderEff
, State
, getEff
, getsEff
, putEff
, modifyEff
, stateEff
, runStateEff
, execStateEff
, evalStateEff
, WriterEff
, writerEff
, tellEff
, listenEff
, passEff
, runWriterEff
, execWriterEff
, MaybeEff
, nothingEff
, runMaybeEff
, EitherEff
, throwEff
, catchEff
, runEitherEff
, Identity
, tickEff
, runIterEff
, ContT
, contEff
, runContEff
) where
import Control.Applicative
import Control.Monad.Skeleton
import Control.Monad.Trans.State.Strict
import Control.Monad.Trans.Cont (ContT(..))
import Data.Extensible.Field
import Data.Extensible.Inclusion
import Data.Extensible.Internal
import Data.Extensible.Internal.Rig
import Data.Extensible.Product
import Data.Extensible.Class
import Data.Functor.Identity
import Data.Profunctor.Unsafe
import Data.Typeable (Typeable)
data Instruction (xs :: [Assoc k (* -> *)]) a where
Instruction :: !(Membership xs kv) -> AssocValue kv a -> Instruction xs a
deriving Typeable
type Eff xs = Skeleton (Instruction xs)
liftEff :: forall s t xs a. Associate s t xs => Proxy s -> t a -> Eff xs a
liftEff p x = liftsEff p x id
{-# INLINE liftEff #-}
liftsEff :: forall s t xs a r. Associate s t xs
=> Proxy s -> t a -> (a -> r) -> Eff xs r
liftsEff _ x k = boned
$ Instruction (association :: Membership xs (s ':> t)) x :>>= return . k
{-# INLINE liftsEff #-}
hoistEff :: forall s t xs a. Associate s t xs => Proxy s -> (forall x. t x -> t x) -> Eff xs a -> Eff xs a
hoistEff _ f = hoistSkeleton $ \(Instruction i t) -> case compareMembership (association :: Membership xs (s ':> t)) i of
Right Refl -> Instruction i (f t)
_ -> Instruction i t
{-# INLINABLE hoistEff #-}
castEff :: IncludeAssoc ys xs => Eff xs a -> Eff ys a
castEff = hoistSkeleton
$ \(Instruction i t) -> Instruction (hlookup i inclusionAssoc) t
peelEff :: forall k t xs a r
. Rebinder xs r
-> (a -> r)
-> (forall x. t x -> (x -> r) -> r)
-> Eff (k >: t ': xs) a -> r
peelEff pass ret wrap = go where
go m = case debone m of
Return a -> ret a
Instruction i t :>>= k -> leadership i
(\Refl -> wrap t (go . k))
(\j -> pass (Instruction j t) (go . k))
{-# INLINE peelEff #-}
peelEff0 :: forall k t xs a r. (a -> Eff xs r)
-> (forall x. t x -> (x -> Eff xs r) -> Eff xs r)
-> Eff (k >: t ': xs) a -> Eff xs r
peelEff0 = peelEff rebindEff0
{-# INLINE peelEff0 #-}
peelEff1 :: forall k t xs a b r. (a -> b -> Eff xs r)
-> (forall x. t x -> (x -> b -> Eff xs r) -> b -> Eff xs r)
-> Eff (k >: t ': xs) a -> b -> Eff xs r
peelEff1 = peelEff rebindEff1
{-# INLINE peelEff1 #-}
type Rebinder xs r = forall x. Instruction xs x -> (x -> r) -> r
rebindEff0 :: Rebinder xs (Eff xs r)
rebindEff0 i k = boned $ i :>>= k
rebindEff1 :: Rebinder xs (a -> Eff xs r)
rebindEff1 i k a = boned $ i :>>= flip k a
rebindEff2 :: Rebinder xs (a -> b -> Eff xs r)
rebindEff2 i k a b = boned $ i :>>= \x -> k x a b
leaveEff :: Eff '[] a -> a
leaveEff m = case debone m of
Return a -> a
_ -> error "Impossible"
retractEff :: forall k m a. Monad m => Eff '[k >: m] a -> m a
retractEff m = case debone m of
Return a -> return a
Instruction i t :>>= k -> leadership i
(\Refl -> t >>= retractEff . k)
$ error "Impossible"
newtype Interpreter f g = Interpreter { runInterpreter :: forall a. g a -> f a }
deriving Typeable
handleEff :: RecordOf (Interpreter m) xs -> Eff xs a -> MonadView m (Eff xs) a
handleEff hs m = case debone m of
Instruction i t :>>= k -> views (pieceAt i) (runInterpreter .# getField) hs t :>>= k
Return a -> Return a
data Action (args :: [*]) a r where
AResult :: Action '[] a a
AArgument :: x -> Action xs a r -> Action (x ': xs) a r
type family Function args r :: * where
Function '[] r = r
Function (x ': xs) r = x -> Function xs r
runAction :: Function xs (f a) -> Action xs a r -> f r
runAction r AResult = r
runAction f (AArgument x a) = runAction (f x) a
(@!?) :: FieldName k -> Function xs (f a) -> Field (Interpreter f) (k ':> Action xs a)
_ @!? f = Field $ Interpreter $ runAction f
infix 1 @!?
peelAction :: forall k ps q xs a r
. (forall x. Instruction xs x -> (x -> r) -> r)
-> (a -> r)
-> Function ps ((q -> r) -> r)
-> Eff (k >: Action ps q ': xs) a -> r
peelAction pass ret wrap = go where
go m = case debone m of
Return a -> ret a
Instruction i t :>>= k -> leadership i
(\Refl -> case t of
(_ :: Action ps q x) ->
let run :: forall t. Function t ((q -> r) -> r) -> Action t q x -> r
run f AResult = f (go . k)
run f (AArgument x a) = run (f x) a
in run wrap t)
$ \j -> pass (Instruction j t) (go . k)
{-# INLINE peelAction #-}
peelAction0 :: forall k ps q xs a. Function ps (Eff xs q)
-> Eff (k >: Action ps q ': xs) a -> Eff xs a
peelAction0 wrap = go where
go m = case debone m of
Return a -> return a
Instruction i t :>>= k -> leadership i
(\Refl -> case t of
(_ :: Action ps q x) ->
let run :: forall t. Function t (Eff xs q) -> Action t q x -> Eff xs a
run f AResult = f >>= go . k
run f (AArgument x a) = run (f x) a
in run wrap t)
$ \j -> rebindEff0 (Instruction j t) (go . k)
{-# INLINE peelAction0 #-}
type ReaderEff = (:~:)
askEff :: forall k r xs. Associate k (ReaderEff r) xs
=> Proxy k -> Eff xs r
askEff p = liftEff p Refl
{-# INLINE askEff #-}
asksEff :: forall k r xs a. Associate k (ReaderEff r) xs
=> Proxy k -> (r -> a) -> Eff xs a
asksEff p = liftsEff p Refl
{-# INLINE asksEff #-}
localEff :: forall k r xs a. Associate k (ReaderEff r) xs
=> Proxy k -> (r -> r) -> Eff xs a -> Eff xs a
localEff _ f = go where
go m = case debone m of
Return a -> return a
Instruction i t :>>= k -> case compareMembership
(association :: Membership xs (k >: ReaderEff r)) i of
Left _ -> boned $ Instruction i t :>>= go . k
Right Refl -> case t of
Refl -> boned $ Instruction i t :>>= go . k . f
{-# INLINE localEff #-}
runReaderEff :: forall k r xs a. Eff (k >: ReaderEff r ': xs) a -> r -> Eff xs a
runReaderEff m r = peelEff0 return (\Refl k -> k r) m
{-# INLINE runReaderEff #-}
getEff :: forall k s xs. Associate k (State s) xs
=> Proxy k -> Eff xs s
getEff k = liftEff k get
{-# INLINE getEff #-}
getsEff :: forall k s a xs. Associate k (State s) xs
=> Proxy k -> (s -> a) -> Eff xs a
getsEff k = liftsEff k get
{-# INLINE getsEff #-}
putEff :: forall k s xs. Associate k (State s) xs
=> Proxy k -> s -> Eff xs ()
putEff k = liftEff k . put
{-# INLINE putEff #-}
modifyEff :: forall k s xs. Associate k (State s) xs
=> Proxy k -> (s -> s) -> Eff xs ()
modifyEff k f = liftEff k $ state $ \s -> ((), f s)
{-# INLINE modifyEff #-}
stateEff :: forall k s xs a. Associate k (State s) xs
=> Proxy k -> (s -> (a, s)) -> Eff xs a
stateEff k = liftEff k . state
{-# INLINE stateEff #-}
contState :: State s a -> (a -> s -> r) -> s -> r
contState m k s = let (a, s') = runState m s in k a $! s'
runStateEff :: forall k s xs a. Eff (k >: State s ': xs) a -> s -> Eff xs (a, s)
runStateEff = peelEff1 (\a s -> return (a, s)) contState
{-# INLINE runStateEff #-}
execStateEff :: forall k s xs a. Eff (k >: State s ': xs) a -> s -> Eff xs s
execStateEff = peelEff1 (const return) contState
{-# INLINE execStateEff #-}
evalStateEff :: forall k s xs a. Eff (k >: State s ': xs) a -> s -> Eff xs a
evalStateEff = peelEff1 (const . return) contState
{-# INLINE evalStateEff #-}
type WriterEff w = (,) w
writerEff :: forall k w xs a. (Associate k (WriterEff w) xs)
=> Proxy k -> (a, w) -> Eff xs a
writerEff k (a, w) = liftEff k (w, a)
{-# INLINE writerEff #-}
tellEff :: forall k w xs. (Associate k (WriterEff w) xs)
=> Proxy k -> w -> Eff xs ()
tellEff k w = liftEff k (w, ())
{-# INLINE tellEff #-}
listenEff :: forall k w xs a. (Associate k (WriterEff w) xs, Monoid w)
=> Proxy k -> Eff xs a -> Eff xs (a, w)
listenEff p = go mempty where
go w m = case debone m of
Return a -> writerEff p ((a, w), w)
Instruction i t :>>= k -> case compareMembership (association :: Membership xs (k ':> (,) w)) i of
Left _ -> boned $ Instruction i t :>>= go w . k
Right Refl -> let (w', a) = t
!w'' = mappend w w' in go w'' (k a)
{-# INLINE listenEff #-}
passEff :: forall k w xs a. (Associate k (WriterEff w) xs, Monoid w)
=> Proxy k -> Eff xs (a, w -> w) -> Eff xs a
passEff p = go mempty where
go w m = case debone m of
Return (a, f) -> writerEff p (a, f w)
Instruction i t :>>= k -> case compareMembership (association :: Membership xs (k ':> (,) w)) i of
Left _ -> boned $ Instruction i t :>>= go w . k
Right Refl -> let (w', a) = t
!w'' = mappend w w' in go w'' (k a)
{-# INLINE passEff #-}
contWriter :: Monoid w => (w, a) -> (a -> w -> r) -> w -> r
contWriter (w', a) k w = k a $! mappend w w'
runWriterEff :: forall k w xs a. Monoid w => Eff (k >: WriterEff w ': xs) a -> Eff xs (a, w)
runWriterEff = peelEff1 (\a w -> return (a, w)) contWriter `flip` mempty
{-# INLINE runWriterEff #-}
execWriterEff :: forall k w xs a. Monoid w => Eff (k >: WriterEff w ': xs) a -> Eff xs w
execWriterEff = peelEff1 (const return) contWriter `flip` mempty
{-# INLINE execWriterEff #-}
type MaybeEff = Const ()
nothingEff :: Associate k MaybeEff xs => Proxy k -> Eff xs a
nothingEff = flip throwEff ()
runMaybeEff :: forall k xs a. Eff (k >: MaybeEff ': xs) a -> Eff xs (Maybe a)
runMaybeEff = peelEff0 (return . Just) $ \_ _ -> return Nothing
{-# INLINE runMaybeEff #-}
type EitherEff = Const
throwEff :: Associate k (EitherEff e) xs => Proxy k -> e -> Eff xs a
throwEff k = liftEff k . Const
{-# INLINE throwEff #-}
catchEff :: forall k e xs a. (Associate k (EitherEff e) xs)
=> Proxy k -> Eff xs a -> (e -> Eff xs a) -> Eff xs a
catchEff _ m0 handler = go m0 where
go m = case debone m of
Return a -> return a
Instruction i t :>>= k -> case compareMembership (association :: Membership xs (k ':> Const e)) i of
Left _ -> boned $ Instruction i t :>>= go . k
Right Refl -> handler (getConst t)
{-# INLINE catchEff #-}
runEitherEff :: forall k e xs a. Eff (k >: EitherEff e ': xs) a -> Eff xs (Either e a)
runEitherEff = peelEff0 (return . Right) $ \(Const e) _ -> return $ Left e
{-# INLINE runEitherEff #-}
tickEff :: Associate k Identity xs => Proxy k -> Eff xs ()
tickEff k = liftEff k $ Identity ()
{-# INLINE tickEff #-}
runIterEff :: Eff (k >: Identity ': xs) a
-> Eff xs (Either a (Eff (k >: Identity ': xs) a))
runIterEff m = case debone m of
Return a -> return (Left a)
Instruction i t :>>= k -> leadership i
(\Refl -> return $ Right $ k $ runIdentity t)
$ \j -> boned $ Instruction j t :>>= runIterEff . k
contEff :: Associate k (ContT r m) xs => Proxy k
-> ((a -> m r) -> m r) -> Eff xs a
contEff k = liftEff k . ContT
runContEff :: forall k r xs a. Eff (k >: ContT r (Eff xs) ': xs) a
-> (a -> Eff xs r)
-> Eff xs r
runContEff m cont = case debone m of
Return a -> cont a
Instruction i t :>>= k -> leadership i
(\Refl -> runContT t (flip runContEff cont . k))
$ \j -> boned $ Instruction j t :>>= flip runContEff cont . k