{-# LANGUAGE UndecidableInstances #-}
module Yael.Eff
( EffT
, pattern EffT
, runEffT
, liftEffT
, shareEffT
, withEffT
, withEffT'
, withEffT''
, Lower
, Raise
, type (~>)
, localEffT
, mapEffT
, HasEff
, HasEffs
, (:+)
, (:<>)(..)
, Project(..)
, HigherOrder(..)
) where
import Control.Lens ((^.), Lens', lens, Field1(_1), Field2(_2), (%~))
import Control.Applicative
import Control.Monad
import Control.Monad.Trans
import qualified Control.Monad.Reader as R
import Control.Exception.Safe
import UnliftIO (MonadUnliftIO(..), withUnliftIO, UnliftIO(..))
import Data.Function ((&))
import GHC.TypeLits
import GHC.Generics
import Control.Monad.Base
import Control.Monad.Trans.Control
newtype EffT (f :: (* -> *) -> *) (m :: * -> *) (a :: *) = MkEffT
{ unEffT :: R.ReaderT (f m) m a
} deriving newtype ( Functor, Applicative, Monad, MonadIO, MonadThrow
, MonadCatch, MonadMask, MonadPlus, Alternative
, MonadBase s, MonadBaseControl s)
pattern EffT :: (f m -> m a) -> EffT f m a
pattern EffT r = MkEffT (R.ReaderT r)
shareEffT :: f m -> (Lower f m m -> EffT f m a) -> m a
shareEffT fm f = runEffT (f $ flip runEffT fm) fm
runEffT
:: forall f m a
. EffT f m a
-> f m
-> m a
runEffT (EffT r) y = r y
liftEffT :: (f m -> m a) -> EffT f m a
liftEffT = EffT
instance MonadUnliftIO m => MonadUnliftIO (EffT f m) where
{-# INLINE askUnliftIO #-}
askUnliftIO = EffT $ \r ->
withUnliftIO $ \u ->
return (UnliftIO (unliftIO u . flip runEffT r))
{-# INLINE withRunInIO #-}
withRunInIO inner =
EffT $ \r ->
withRunInIO $ \run ->
inner (run . flip runEffT r)
instance MonadTrans (EffT f) where
lift = MkEffT . lift
withEffT :: (HasEff g f m) => (forall n . Monad n => g n -> n a) -> EffT f m a
withEffT use = withEffT' $ const use
withEffT'
:: (HasEff g f m)
=> (forall n . Monad n => Lower f m n -> g n -> n a)
-> EffT f m a
withEffT' use = withEffT'' $ \lower _ g -> use lower g
type f ~> g = forall x . f x -> g x
type Lower f m n = EffT f m ~> n
type Raise f m n = n ~> EffT f m
withEffT''
:: (HasEff g f m)
=> (forall n
. Monad n
=> Lower f m n
-> Raise f m n
-> g n
-> n a
)
-> EffT f m a
withEffT'' use = EffT $ \f -> use (\e -> runEffT e f) lift (f ^. prj)
localEffT :: Project f g => (g m -> g m) -> EffT f m a -> EffT f m a
localEffT modify (EffT r) = EffT $ \f -> r $ f & prj %~ modify
mapEffT
:: Monad m
=> (f m -> g m)
-> EffT g m a
-> EffT f m a
mapEffT f (EffT r) = EffT $ \g -> r (f g)
class Project (f :: (* -> *) -> *) g where
prj :: Lens' (f m) (g m)
instance {-# OVERLAPPING #-} Project x x where
prj = id
infixr :<>
data (a :<> b) (m :: * -> *) = a m :<> b m
deriving (Show, Generic)
instance Field1 ((a :<> b) m) ((a' :<> b) m) (a m) (a' m)
instance Field2 ((a :<> b) m) ((a :<> b') m) (b m) (b' m)
instance {-# OVERLAPPING #-} Project ((a :<> b)) a where
prj = _1
instance {-# OVERLAPPABLE #-} Project b c => Project ((a :<> b)) c where
prj = _2 . (prj @b @c)
type family MissingError x y where
MissingError x f =
'Text "Expected a handler for " ':<>: 'ShowType f ':<>: 'Text " to provided through `runEffT`"
':$$: 'Text "The handlers available are: " ':<>: ShowStack (Stacks x)
type family Stacks x where
Stacks (f :<> g) = f ': (Stacks g)
Stacks f = '[f]
type family ShowStack (xs :: [k]) where
ShowStack '[x] = 'ShowType x
ShowStack (x ': xs) = 'ShowType x ':<>: 'Text ", " ':<>: ShowStack xs
instance
{-# OVERLAPPABLE #-}
(TypeError (MissingError x y)) =>
Project x y where
prj = error "Missing implementation! This should be a type error"
type HasEff g f m = (Project f g, Monad m)
type family HasEffs xs f m where
HasEffs '[x] f m = HasEff x f m
HasEffs (x ': xs) f m = (HasEff x f m, HasEffs xs f m)
infix 7 :+
type (:+) v effs = forall m f . (HasEffs effs f m) => EffT f m v
newtype HigherOrder n (m :: * -> *) = HigherOrder
{ applyHigherOrder :: forall a . n a -> m a
}