@@ -16,8 +16,7 @@ module Effectful.Internal.Monad
16 , unsafeEff_
16 , unsafeEff_
@@ -40,14 +39,25 @@ module Effectful.Internal.Monad
40 , seqUnliftEff
39 , seqUnliftEff
41 , concUnliftEff
40 , concUnliftEff
43− -- * Primitive effects
42+ -- * Effects
50+ -- *** Sending operations
45− -- ** Running
55+ -- *** Running
46 , runEffect
56 , runEffect
47 , evalEffect
57 , evalEffect
48 , execEffect
58 , execEffect
50− -- ** Modification
60+ -- *** Modification
51 , getEffect
61 , getEffect
52 , putEffect
62 , putEffect
53 , stateEffect
63 , stateEffect
@@ -57,19 +67,18 @@ module Effectful.Internal.Monad
58import Control.Applicative (liftA2)
68import Control.Applicative (liftA2)
59import Control.Concurrent (myThreadId)
69import Control.Concurrent (myThreadId)
60−import Control.Exception
61import Control.Monad.Base
70import Control.Monad.Base
62import Control.Monad.Fix
71import Control.Monad.Fix
63import Control.Monad.IO.Class
72import Control.Monad.IO.Class
64import Control.Monad.IO.Unlift
73import Control.Monad.IO.Unlift
65import Control.Monad.Primitive
74import Control.Monad.Primitive
66import Control.Monad.Trans.Control
75import Control.Monad.Trans.Control
68import GHC.Exts (oneShot)
76import GHC.Exts (oneShot)
69import GHC.IO (IO(..))
77import GHC.IO (IO(..))
70import GHC.Stack (HasCallStack)
78import GHC.Stack (HasCallStack)
71import System.IO.Unsafe (unsafeDupablePerformIO)
79import System.IO.Unsafe (unsafeDupablePerformIO)
72−import qualified Control.Monad.Catch as E
80+import qualified Control.Exception as E
81+import qualified Control.Monad.Catch as C
74import Effectful.Internal.Effect
83import Effectful.Internal.Effect
75import Effectful.Internal.Env
84import Effectful.Internal.Env
@@ -218,71 +227,43 @@ instance MonadFix (Eff es) where
218----------------------------------------
227----------------------------------------
219-- Exception
228-- Exception
221−instance E.MonadThrow (Eff es) where
230+instance C.MonadThrow (Eff es) where
222− throwM = unsafeEff_ . throwIO
231+ throwM = unsafeEff_ . E.throwIO
224−instance E.MonadCatch (Eff es) where
233+instance C.MonadCatch (Eff es) where
225 catch m handler = unsafeEff $ \es -> do
234 catch m handler = unsafeEff $ \es -> do
226 size <- sizeEnv es
235 size <- sizeEnv es
227− unEff m es `catch` \e -> do
236+ unEff m es `E.catch` \e -> do
228 checkSizeEnv size es
237 checkSizeEnv size es
229 unEff (handler e) es
238 unEff (handler e) es
231−instance E.MonadMask (Eff es) where
240+instance C.MonadMask (Eff es) where
232− mask k = unsafeEff $ \es -> mask $ \restore ->
241+ mask k = unsafeEff $ \es -> E.mask $ \restore ->
233 unEff (k $ \m -> unsafeEff $ restore . unEff m) es
242 unEff (k $ \m -> unsafeEff $ restore . unEff m) es
235− uninterruptibleMask k = unsafeEff $ \es -> uninterruptibleMask $ \restore ->
244+ uninterruptibleMask k = unsafeEff $ \es -> E.uninterruptibleMask $ \restore ->
236 unEff (k $ \m -> unsafeEff $ restore . unEff m) es
245 unEff (k $ \m -> unsafeEff $ restore . unEff m) es
238− generalBracket acquire release use = unsafeEff $ \es -> mask $ \restore -> do
247+ generalBracket acquire release use = unsafeEff $ \es -> E.mask $ \restore -> do
239 size <- sizeEnv es
248 size <- sizeEnv es
240 resource <- unEff acquire es
249 resource <- unEff acquire es
241− b <- restore (unEff (use resource) es) `catch` \e -> do
250+ b <- restore (unEff (use resource) es) `E.catch` \e -> do
242 checkSizeEnv size es
251 checkSizeEnv size es
243− _ <- unEff (release resource $ E.ExitCaseException e) es
252+ _ <- unEff (release resource $ C.ExitCaseException e) es
244− throwIO e
253+ E.throwIO e
245 checkSizeEnv size es
254 checkSizeEnv size es
246− c <- unEff (release resource $ E.ExitCaseSuccess b) es
255+ c <- unEff (release resource $ C.ExitCaseSuccess b) es
247 pure (b, c)
256 pure (b, c)
249----------------------------------------
258----------------------------------------
261+-- | Provide the ability to use the 'MonadFail' instance of 'Eff'.
252−newtype Fail :: Effect where
262+data Fail :: Effect where
253− Fail :: Unique -> Fail m r
263+ Fail :: String -> Fail m a
255−runFail :: Eff (Fail : es) a -> Eff es (Either String a)
256−runFail m = unsafeEff $ \es0 -> mask $ \release -> do
257− -- A unique tag is picked so that different runFail handlers don't catch each
258− -- other's exceptions.
260− size0 <- sizeEnv es0
261− es <- unsafeConsEnv (IdE (Fail tag)) noRelinker es0
262− r <- tryFailIO tag (release $ unEff m es) `onException` unsafeTailEnv size0 es
263− unsafeTailEnv size0 es
266instance Fail :> es => MonadFail (Eff es) where
265instance Fail :> es => MonadFail (Eff es) where
267− fail msg = do
266+ fail = send . Fail
268− IdE (Fail tag) <- getEffect
269− unsafeEff_ . throwIO $ FailEx tag msg
273−data FailEx = FailEx !Unique String
274−instance Show FailEx where
275− showsPrec p (FailEx _ msg)
276− = ("Effectful.Internal.Monad.FailEx " ++)
278−instance Exception FailEx
280−tryFailIO :: Unique -> IO a -> IO (Either String a)
282− (Right <$> m) `catch` \err@(FailEx etag msg) -> do
284− then pure $ Left msg
287----------------------------------------
268----------------------------------------
@@ -413,29 +394,70 @@ withEffToIO f = unliftStrategy >>= \case
413 ConcUnlift p b -> withUnliftStrategy SeqUnlift $ do
394 ConcUnlift p b -> withUnliftStrategy SeqUnlift $ do
414 unsafeEff $ \es -> concUnliftEff es p b f
395 unsafeEff $ \es -> concUnliftEff es p b f
397+----------------------------------------
400+-- | Opaque representation of the 'Eff' environment at the point of calling the
401+-- 'send' function, i.e. right before the control is passed to the effect
403+newtype LocalEnv es = LocalEnv (Env es)
405+-- | Type signature of the effect handler.
406+type EffectHandler e es
407+ = forall a localEs. HasCallStack
409+ -- ^ Capture of the local environment for handling local 'Eff' operations when
410+ -- @e@ is a higher order effect.
411+ -> e (Eff localEs) a
412+ -- ^ The effect performed in the local environment.
415+-- | An effect handler bundled along with its environment.
416+data Handler :: Effect -> Type where
417+ Handler :: !(Env es) -> !(EffectHandler e es) -> Handler e
419+-- | Run the effect with the given handler.
420+runHandler :: Handler e -> Eff (e : es) a -> Eff es a
421+runHandler e m = unsafeEff $ \es0 -> do
422+ size0 <- sizeEnv es0
423+ E.bracket (unsafeConsEnv e relinker es0)
424+ (unsafeTailEnv size0)
427+ relinker :: Relinker Handler e
428+ relinker = Relinker $ \relink (Handler env handle) -> do
429+ newEnv <- relink env
430+ pure $ Handler newEnv handle
432+-- | Send an operation of the given effect to its handler for execution.
433+send :: (HasCallStack, e :> es) => e (Eff es) a -> Eff es a
434+send op = unsafeEff $ \es -> do
435+ Handler env handle <- getEnv es
436+ unEff (handle (LocalEnv es) op) env
416----------------------------------------
438----------------------------------------
417-- Helpers
439-- Helpers
419runEffect :: handler e -> Eff (e : es) a -> Eff es (a, handler e)
441runEffect :: handler e -> Eff (e : es) a -> Eff es (a, handler e)
420runEffect e0 m = unsafeEff $ \es0 -> do
442runEffect e0 m = unsafeEff $ \es0 -> do
421 size0 <- sizeEnv es0
443 size0 <- sizeEnv es0
422− bracket (unsafeConsEnv e0 noRelinker es0)
444+ E.bracket (unsafeConsEnv e0 noRelinker es0)
423− (unsafeTailEnv size0)
445+ (unsafeTailEnv size0)
424− (\es -> (,) <$> unEff m es <*> getEnv es)
446+ (\es -> (,) <$> unEff m es <*> getEnv es)
426evalEffect :: handler e -> Eff (e : es) a -> Eff es a
448evalEffect :: handler e -> Eff (e : es) a -> Eff es a
427evalEffect e m = unsafeEff $ \es0 -> do
449evalEffect e m = unsafeEff $ \es0 -> do
428 size0 <- sizeEnv es0
450 size0 <- sizeEnv es0
429− bracket (unsafeConsEnv e noRelinker es0)
451+ E.bracket (unsafeConsEnv e noRelinker es0)
430− (unsafeTailEnv size0)
452+ (unsafeTailEnv size0)
431− (\es -> unEff m es)
453+ (\es -> unEff m es)
433execEffect :: handler e -> Eff (e : es) a -> Eff es (handler e)
455execEffect :: handler e -> Eff (e : es) a -> Eff es (handler e)
434execEffect e0 m = unsafeEff $ \es0 -> do
456execEffect e0 m = unsafeEff $ \es0 -> do
435 size0 <- sizeEnv es0
457 size0 <- sizeEnv es0
436− bracket (unsafeConsEnv e0 noRelinker es0)
458+ E.bracket (unsafeConsEnv e0 noRelinker es0)
437− (unsafeTailEnv size0)
459+ (unsafeTailEnv size0)
438− (\es -> unEff m es *> getEnv es)
460+ (\es -> unEff m es *> getEnv es)
440getEffect :: e :> es => Eff es (handler e)
462getEffect :: e :> es => Eff es (handler e)
441getEffect = unsafeEff $ \es -> getEnv es
463getEffect = unsafeEff $ \es -> getEnv es
@@ -447,13 +469,13 @@ stateEffect :: e :> es => (handler e -> (a, handler e)) -> Eff es a
447stateEffect f = unsafeEff $ \es -> stateEnv es f
469stateEffect f = unsafeEff $ \es -> stateEnv es f
449stateEffectM :: e :> es => (handler e -> Eff es (a, handler e)) -> Eff es a
471stateEffectM :: e :> es => (handler e -> Eff es (a, handler e)) -> Eff es a
450−stateEffectM f = unsafeEff $ \es -> mask $ \release -> do
472+stateEffectM f = unsafeEff $ \es -> E.mask $ \release -> do
451 (a, e) <- (\e -> release $ unEff (f e) es) =<< getEnv es
473 (a, e) <- (\e -> release $ unEff (f e) es) =<< getEnv es
452 putEnv es e
474 putEnv es e
455localEffect :: e :> es => (handler e -> handler e) -> Eff es a -> Eff es a
477localEffect :: e :> es => (handler e -> handler e) -> Eff es a -> Eff es a
456localEffect f m = unsafeEff $ \es -> do
478localEffect f m = unsafeEff $ \es -> do
457− bracket (stateEnv es $ \e -> (e, f e))
479+ E.bracket (stateEnv es $ \e -> (e, f e))
458− (\e -> putEnv es e)
480+ (\e -> putEnv es e)
459− (\_ -> unEff m es)
481+ (\_ -> unEff m es)