{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module Control.Method
(
Method (..),
TupleLike (..),
decorate,
decorate_,
decorateBefore_,
invoke,
liftJoin,
NT,
Interface (..),
mapBaseRIO,
)
where
import Control.Exception (SomeException)
import Control.Method.Internal
( Nil (Nil),
TupleLike (AsTuple, fromTuple, toTuple),
type (:*) ((:*)),
)
import Control.Monad.Trans.Accum (AccumT)
import Control.Monad.Trans.Cont (ContT)
import Control.Monad.Trans.Except (ExceptT)
import Control.Monad.Trans.Maybe (MaybeT)
import qualified Control.Monad.Trans.RWS.CPS as CPS
import qualified Control.Monad.Trans.RWS.Lazy as Lazy
import qualified Control.Monad.Trans.RWS.Strict as Strict
import Control.Monad.Trans.Reader (ReaderT)
import Control.Monad.Trans.Select (SelectT)
import qualified Control.Monad.Trans.State.Lazy as Lazy
import qualified Control.Monad.Trans.State.Strict as Strict
import qualified Control.Monad.Trans.Writer.CPS as CPS
import qualified Control.Monad.Trans.Writer.Lazy as Lazy
import qualified Control.Monad.Trans.Writer.Strict as Strict
import Data.Functor.Identity (Identity)
import Data.Kind (Type)
import GHC.Generics
( Generic (Rep, from, to),
K1 (K1),
M1 (M1),
type (:*:) ((:*:)),
type (:+:) (L1, R1),
)
import RIO (MonadIO (liftIO), MonadReader (ask), MonadUnliftIO, RIO, ST, SimpleGetter, runRIO, throwIO, tryAny, view)
class Monad (Base method) => Method method where
type Base method :: Type -> Type
type Args method :: Type
type Args method = Nil
type Ret method :: Type
uncurryMethod :: method -> Args method -> Base method (Ret method)
{-# INLINE uncurryMethod #-}
default uncurryMethod ::
(method ~ Base method a, Args method ~ Nil, Ret method ~ a) =>
method ->
Args method ->
Base method (Ret method)
uncurryMethod method
method Args method
Nil = method
Base method (Ret method)
method
curryMethod :: (Args method -> Base method (Ret method)) -> method
{-# INLINE curryMethod #-}
default curryMethod ::
(method ~ Base method a, Args method ~ Nil, Ret method ~ a) =>
(Args method -> Base method (Ret method)) ->
method
curryMethod Args method -> Base method (Ret method)
method' = Args method -> Base method (Ret method)
method' Nil
Args method
Nil
{-# INLINE liftJoin #-}
liftJoin :: Method method => Base method method -> method
liftJoin :: Base method method -> method
liftJoin Base method method
mMethod = (Args method -> Base method (Ret method)) -> method
forall method.
Method method =>
(Args method -> Base method (Ret method)) -> method
curryMethod ((Args method -> Base method (Ret method)) -> method)
-> (Args method -> Base method (Ret method)) -> method
forall a b. (a -> b) -> a -> b
$ \Args method
args -> do
method
method <- Base method method
mMethod
method -> Args method -> Base method (Ret method)
forall method.
Method method =>
method -> Args method -> Base method (Ret method)
uncurryMethod method
method Args method
args
instance Method (IO a) where
type Base (IO a) = IO
type Ret (IO a) = a
instance Method (RIO env a) where
type Base (RIO env a) = RIO env
type Ret (RIO env a) = a
instance Method (Identity a) where
type Base (Identity a) = Identity
type Ret (Identity a) = a
instance Method (Maybe a) where
type Base (Maybe a) = Maybe
type Ret (Maybe a) = a
instance Method [a] where
type Base [a] = []
type Ret [a] = a
instance Method (Either e a) where
type Base (Either e a) = Either e
type Ret (Either e a) = a
instance Method (ST s a) where
type Base (ST s a) = ST s
type Ret (ST s a) = a
instance (Monoid w, Monad m) => Method (AccumT w m a) where
type Base (AccumT w m a) = AccumT w m
type Ret (AccumT w m a) = a
instance (Monad m) => Method (ContT r m a) where
type Base (ContT r m a) = ContT r m
type Ret (ContT r m a) = a
instance (Monad m) => Method (ExceptT e m a) where
type Base (ExceptT e m a) = ExceptT e m
type Ret (ExceptT e m a) = a
instance (Monad m) => Method (MaybeT m a) where
type Base (MaybeT m a) = MaybeT m
type Ret (MaybeT m a) = a
instance (Monad m) => Method (CPS.RWST r w s m a) where
type Base (CPS.RWST r w s m a) = CPS.RWST r w s m
type Ret (CPS.RWST r w s m a) = a
instance (Monad m, Monoid w) => Method (Lazy.RWST r w s m a) where
type Base (Lazy.RWST r w s m a) = Lazy.RWST r w s m
type Ret (Lazy.RWST r w s m a) = a
instance (Monad m, Monoid w) => Method (Strict.RWST r w s m a) where
type Base (Strict.RWST r w s m a) = Strict.RWST r w s m
type Ret (Strict.RWST r w s m a) = a
instance Monad m => Method (ReaderT r m a) where
type Base (ReaderT r m a) = ReaderT r m
type Ret (ReaderT r m a) = a
instance Monad m => Method (SelectT r m a) where
type Base (SelectT r m a) = SelectT r m
type Ret (SelectT r m a) = a
instance Monad m => Method (Lazy.StateT s m a) where
type Base (Lazy.StateT s m a) = Lazy.StateT s m
type Ret (Lazy.StateT s m a) = a
instance Monad m => Method (Strict.StateT s m a) where
type Base (Strict.StateT s m a) = Strict.StateT s m
type Ret (Strict.StateT s m a) = a
instance (Monad m) => Method (CPS.WriterT w m a) where
type Base (CPS.WriterT w m a) = CPS.WriterT w m
type Ret (CPS.WriterT w m a) = a
instance (Monad m, Monoid w) => Method (Lazy.WriterT w m a) where
type Base (Lazy.WriterT w m a) = Lazy.WriterT w m
type Ret (Lazy.WriterT w m a) = a
instance (Monad m, Monoid w) => Method (Strict.WriterT w m a) where
type Base (Strict.WriterT w m a) = Strict.WriterT w m
type Ret (Strict.WriterT w m a) = a
instance Method b => Method (a -> b) where
type Base (a -> b) = Base b
type Args (a -> b) = a :* Args b
type Ret (a -> b) = Ret b
{-# INLINE uncurryMethod #-}
uncurryMethod :: (a -> b) -> Args (a -> b) -> Base (a -> b) (Ret (a -> b))
uncurryMethod a -> b
method (a :* args) = b -> Args b -> Base b (Ret b)
forall method.
Method method =>
method -> Args method -> Base method (Ret method)
uncurryMethod (a -> b
method a
a) Args b
args
{-# INLINE curryMethod #-}
curryMethod :: (Args (a -> b) -> Base (a -> b) (Ret (a -> b))) -> a -> b
curryMethod Args (a -> b) -> Base (a -> b) (Ret (a -> b))
method' a
a = (Args b -> Base b (Ret b)) -> b
forall method.
Method method =>
(Args method -> Base method (Ret method)) -> method
curryMethod (\Args b
args -> Args (a -> b) -> Base (a -> b) (Ret (a -> b))
method' (a
a a -> Args b -> a :* Args b
forall a b. a -> b -> a :* b
:* Args b
args))
{-# INLINE decorate #-}
decorate ::
(Method method, MonadUnliftIO (Base method)) =>
(Args method -> Base method a) ->
(a -> Either SomeException (Ret method) -> Base method ()) ->
(a -> method) ->
method
decorate :: (Args method -> Base method a)
-> (a -> Either SomeException (Ret method) -> Base method ())
-> (a -> method)
-> method
decorate Args method -> Base method a
before a -> Either SomeException (Ret method) -> Base method ()
after a -> method
method = (Args method -> Base method (Ret method)) -> method
forall method.
Method method =>
(Args method -> Base method (Ret method)) -> method
curryMethod ((Args method -> Base method (Ret method)) -> method)
-> (Args method -> Base method (Ret method)) -> method
forall a b. (a -> b) -> a -> b
$ \Args method
args -> do
a
a <- Args method -> Base method a
before Args method
args
Either SomeException (Ret method)
res <- Base method (Ret method)
-> Base method (Either SomeException (Ret method))
forall (m :: * -> *) a.
MonadUnliftIO m =>
m a -> m (Either SomeException a)
tryAny (method -> Args method -> Base method (Ret method)
forall method.
Method method =>
method -> Args method -> Base method (Ret method)
uncurryMethod (a -> method
method a
a) Args method
args)
case Either SomeException (Ret method)
res of
Left SomeException
err -> a -> Either SomeException (Ret method) -> Base method ()
after a
a Either SomeException (Ret method)
res Base method ()
-> Base method (Ret method) -> Base method (Ret method)
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> SomeException -> Base method (Ret method)
forall (m :: * -> *) e a. (MonadIO m, Exception e) => e -> m a
throwIO SomeException
err
Right Ret method
v -> a -> Either SomeException (Ret method) -> Base method ()
after a
a Either SomeException (Ret method)
res Base method ()
-> Base method (Ret method) -> Base method (Ret method)
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Ret method -> Base method (Ret method)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Ret method
v
{-# INLINE decorate_ #-}
decorate_ ::
(Method method, MonadUnliftIO (Base method)) =>
(Args method -> Base method ()) ->
(Either SomeException (Ret method) -> Base method ()) ->
method ->
method
decorate_ :: (Args method -> Base method ())
-> (Either SomeException (Ret method) -> Base method ())
-> method
-> method
decorate_ Args method -> Base method ()
before Either SomeException (Ret method) -> Base method ()
after method
method = (Args method -> Base method (Ret method)) -> method
forall method.
Method method =>
(Args method -> Base method (Ret method)) -> method
curryMethod ((Args method -> Base method (Ret method)) -> method)
-> (Args method -> Base method (Ret method)) -> method
forall a b. (a -> b) -> a -> b
$ \Args method
args -> do
Args method -> Base method ()
before Args method
args
Either SomeException (Ret method)
res <- Base method (Ret method)
-> Base method (Either SomeException (Ret method))
forall (m :: * -> *) a.
MonadUnliftIO m =>
m a -> m (Either SomeException a)
tryAny (method -> Args method -> Base method (Ret method)
forall method.
Method method =>
method -> Args method -> Base method (Ret method)
uncurryMethod method
method Args method
args)
case Either SomeException (Ret method)
res of
Left SomeException
err -> Either SomeException (Ret method) -> Base method ()
after Either SomeException (Ret method)
res Base method ()
-> Base method (Ret method) -> Base method (Ret method)
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> SomeException -> Base method (Ret method)
forall (m :: * -> *) e a. (MonadIO m, Exception e) => e -> m a
throwIO SomeException
err
Right Ret method
v -> Either SomeException (Ret method) -> Base method ()
after Either SomeException (Ret method)
res Base method ()
-> Base method (Ret method) -> Base method (Ret method)
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Ret method -> Base method (Ret method)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Ret method
v
{-# INLINE decorateBefore_ #-}
decorateBefore_ ::
(Method method) =>
(Args method -> Base method ()) ->
method ->
method
decorateBefore_ :: (Args method -> Base method ()) -> method -> method
decorateBefore_ Args method -> Base method ()
before method
method = (Args method -> Base method (Ret method)) -> method
forall method.
Method method =>
(Args method -> Base method (Ret method)) -> method
curryMethod ((Args method -> Base method (Ret method)) -> method)
-> (Args method -> Base method (Ret method)) -> method
forall a b. (a -> b) -> a -> b
$ \Args method
args -> do
Args method -> Base method ()
before Args method
args
method -> Args method -> Base method (Ret method)
forall method.
Method method =>
method -> Args method -> Base method (Ret method)
uncurryMethod method
method Args method
args
{-# INLINE invoke #-}
invoke :: (MonadReader env (Base method), Method method) => SimpleGetter env method -> method
invoke :: SimpleGetter env method -> method
invoke SimpleGetter env method
getter = Base method method -> method
forall method. Method method => Base method method -> method
liftJoin (Getting method env method -> Base method method
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting method env method
SimpleGetter env method
getter)
type NT m n = forall a. m a -> n a
class LiftNT f g where
type BaseFrom f :: Type -> Type
type BaseTo g :: Type -> Type
liftNT :: NT (BaseFrom f) (BaseTo g) -> f p -> g p
instance (Method c1, Method c2, Args c1 ~ Args c2, Ret c1 ~ Ret c2) => LiftNT (K1 i1 c1) (K1 i2 c2) where
type BaseFrom (K1 i1 c1) = Base c1
type BaseTo (K1 i2 c2) = Base c2
{-# INLINE liftNT #-}
liftNT :: NT (BaseFrom (K1 i1 c1)) (BaseTo (K1 i2 c2))
-> K1 i1 c1 p -> K1 i2 c2 p
liftNT NT (BaseFrom (K1 i1 c1)) (BaseTo (K1 i2 c2))
nt (K1 c1
s) = c2 -> K1 i2 c2 p
forall k i c (p :: k). c -> K1 i c p
K1 (c2 -> K1 i2 c2 p) -> c2 -> K1 i2 c2 p
forall a b. (a -> b) -> a -> b
$
(Args c2 -> Base c2 (Ret c2)) -> c2
forall method.
Method method =>
(Args method -> Base method (Ret method)) -> method
curryMethod ((Args c2 -> Base c2 (Ret c2)) -> c2)
-> (Args c2 -> Base c2 (Ret c2)) -> c2
forall a b. (a -> b) -> a -> b
$ \Args c2
args ->
BaseFrom (K1 i1 c1) (Ret c2) -> BaseTo (K1 i2 c2) (Ret c2)
NT (BaseFrom (K1 i1 c1)) (BaseTo (K1 i2 c2))
nt (BaseFrom (K1 i1 c1) (Ret c2) -> BaseTo (K1 i2 c2) (Ret c2))
-> BaseFrom (K1 i1 c1) (Ret c2) -> BaseTo (K1 i2 c2) (Ret c2)
forall a b. (a -> b) -> a -> b
$ c1 -> Args c1 -> Base c1 (Ret c1)
forall method.
Method method =>
method -> Args method -> Base method (Ret method)
uncurryMethod c1
s Args c1
Args c2
args
instance LiftNT f1 f2 => LiftNT (M1 i1 t1 f1) (M1 i2 t2 f2) where
type BaseFrom (M1 i1 t1 f1) = BaseFrom f1
type BaseTo (M1 i2 t2 f2) = BaseTo f2
{-# INLINE liftNT #-}
liftNT :: NT (BaseFrom (M1 i1 t1 f1)) (BaseTo (M1 i2 t2 f2))
-> M1 i1 t1 f1 p -> M1 i2 t2 f2 p
liftNT NT (BaseFrom (M1 i1 t1 f1)) (BaseTo (M1 i2 t2 f2))
nt (M1 f1 p
f1) = f2 p -> M1 i2 t2 f2 p
forall k i (c :: Meta) (f :: k -> *) (p :: k). f p -> M1 i c f p
M1 (f2 p -> M1 i2 t2 f2 p) -> f2 p -> M1 i2 t2 f2 p
forall a b. (a -> b) -> a -> b
$ NT (BaseFrom f1) (BaseTo f2) -> f1 p -> f2 p
forall k (f :: k -> *) (g :: k -> *) (p :: k).
LiftNT f g =>
NT (BaseFrom f) (BaseTo g) -> f p -> g p
liftNT NT (BaseFrom f1) (BaseTo f2)
NT (BaseFrom (M1 i1 t1 f1)) (BaseTo (M1 i2 t2 f2))
nt f1 p
f1
instance
(LiftNT f1 g1, LiftNT f2 g2, BaseFrom f1 ~ BaseFrom f2, BaseTo g1 ~ BaseTo g2) =>
LiftNT (f1 :*: f2) (g1 :*: g2)
where
type BaseFrom (f1 :*: f2) = BaseFrom f1
type BaseTo (g1 :*: g2) = BaseTo g1
{-# INLINE liftNT #-}
liftNT :: NT (BaseFrom (f1 :*: f2)) (BaseTo (g1 :*: g2))
-> (:*:) f1 f2 p -> (:*:) g1 g2 p
liftNT NT (BaseFrom (f1 :*: f2)) (BaseTo (g1 :*: g2))
nt (f1 p
f1 :*: f2 p
f2) = NT (BaseFrom f1) (BaseTo g1) -> f1 p -> g1 p
forall k (f :: k -> *) (g :: k -> *) (p :: k).
LiftNT f g =>
NT (BaseFrom f) (BaseTo g) -> f p -> g p
liftNT NT (BaseFrom f1) (BaseTo g1)
NT (BaseFrom (f1 :*: f2)) (BaseTo (g1 :*: g2))
nt f1 p
f1 g1 p -> g2 p -> (:*:) g1 g2 p
forall k (f :: k -> *) (g :: k -> *) (p :: k).
f p -> g p -> (:*:) f g p
:*: NT (BaseFrom f2) (BaseTo g2) -> f2 p -> g2 p
forall k (f :: k -> *) (g :: k -> *) (p :: k).
LiftNT f g =>
NT (BaseFrom f) (BaseTo g) -> f p -> g p
liftNT NT (BaseFrom f2) (BaseTo g2)
NT (BaseFrom (f1 :*: f2)) (BaseTo (g1 :*: g2))
nt f2 p
f2
instance
(LiftNT f1 g1, LiftNT f2 g2, BaseFrom f1 ~ BaseFrom f2, BaseTo g1 ~ BaseTo g2) =>
LiftNT (f1 :+: f2) (g1 :+: g2)
where
type BaseFrom (f1 :+: f2) = BaseFrom f1
type BaseTo (g1 :+: g2) = BaseTo g1
{-# INLINE liftNT #-}
liftNT :: NT (BaseFrom (f1 :+: f2)) (BaseTo (g1 :+: g2))
-> (:+:) f1 f2 p -> (:+:) g1 g2 p
liftNT NT (BaseFrom (f1 :+: f2)) (BaseTo (g1 :+: g2))
nt (L1 f1 p
f1) = g1 p -> (:+:) g1 g2 p
forall k (f :: k -> *) (g :: k -> *) (p :: k). f p -> (:+:) f g p
L1 (g1 p -> (:+:) g1 g2 p) -> g1 p -> (:+:) g1 g2 p
forall a b. (a -> b) -> a -> b
$ NT (BaseFrom f1) (BaseTo g1) -> f1 p -> g1 p
forall k (f :: k -> *) (g :: k -> *) (p :: k).
LiftNT f g =>
NT (BaseFrom f) (BaseTo g) -> f p -> g p
liftNT NT (BaseFrom f1) (BaseTo g1)
NT (BaseFrom (f1 :+: f2)) (BaseTo (g1 :+: g2))
nt f1 p
f1
liftNT NT (BaseFrom (f1 :+: f2)) (BaseTo (g1 :+: g2))
nt (R1 f2 p
f2) = g2 p -> (:+:) g1 g2 p
forall k (f :: k -> *) (g :: k -> *) (p :: k). g p -> (:+:) f g p
R1 (g2 p -> (:+:) g1 g2 p) -> g2 p -> (:+:) g1 g2 p
forall a b. (a -> b) -> a -> b
$ NT (BaseFrom f2) (BaseTo g2) -> f2 p -> g2 p
forall k (f :: k -> *) (g :: k -> *) (p :: k).
LiftNT f g =>
NT (BaseFrom f) (BaseTo g) -> f p -> g p
liftNT NT (BaseFrom f2) (BaseTo g2)
NT (BaseFrom (f1 :+: f2)) (BaseTo (g1 :+: g2))
nt f2 p
f2
class Interface (iface :: k -> Type) where
type IBase iface :: k -> Type -> Type
mapBase :: NT (IBase iface p) (IBase iface q) -> iface p -> iface q
default mapBase ::
( Generic (iface p),
Generic (iface q),
LiftNT (Rep (iface p)) (Rep (iface q)),
BaseFrom (Rep (iface p)) ~ IBase iface p,
BaseTo (Rep (iface q)) ~ IBase iface q
) =>
NT (IBase iface p) (IBase iface q) ->
iface p ->
iface q
mapBase NT (IBase iface p) (IBase iface q)
nt iface p
s = Rep (iface q) Any -> iface q
forall a x. Generic a => Rep a x -> a
to (Rep (iface q) Any -> iface q) -> Rep (iface q) Any -> iface q
forall a b. (a -> b) -> a -> b
$ NT (BaseFrom (Rep (iface p))) (BaseTo (Rep (iface q)))
-> Rep (iface p) Any -> Rep (iface q) Any
forall k (f :: k -> *) (g :: k -> *) (p :: k).
LiftNT f g =>
NT (BaseFrom f) (BaseTo g) -> f p -> g p
liftNT NT (IBase iface p) (IBase iface q)
NT (BaseFrom (Rep (iface p))) (BaseTo (Rep (iface q)))
nt (Rep (iface p) Any -> Rep (iface q) Any)
-> Rep (iface p) Any -> Rep (iface q) Any
forall a b. (a -> b) -> a -> b
$ iface p -> Rep (iface p) Any
forall a x. Generic a => a -> Rep a x
from iface p
s
mapBaseRIO :: (Interface iface, IBase iface ~ RIO) => (env -> env') -> iface env' -> iface env
mapBaseRIO :: (env -> env') -> iface env' -> iface env
mapBaseRIO env -> env'
f = NT (IBase iface env') (IBase iface env) -> iface env' -> iface env
forall k (iface :: k -> *) (p :: k) (q :: k).
Interface iface =>
NT (IBase iface p) (IBase iface q) -> iface p -> iface q
mapBase (\IBase iface env' a
m -> RIO env env
forall r (m :: * -> *). MonadReader r m => m r
ask RIO env env -> (env -> RIO env a) -> RIO env a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \env
env -> IO a -> RIO env a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO a -> RIO env a) -> IO a -> RIO env a
forall a b. (a -> b) -> a -> b
$ env' -> RIO env' a -> IO a
forall (m :: * -> *) env a. MonadIO m => env -> RIO env a -> m a
runRIO (env -> env'
f env
env) RIO env' a
IBase iface env' a
m)