@@ -1,5 +1,5 @@
1module Effectful.Interpreter
1module Effectful.Interpreter
2− ( -- * Sending commands
2+ ( -- * Sending operations to the handler
5 -- * Handling effects
5 -- * Handling effects
@@ -12,21 +12,26 @@ module Effectful.Interpreter
12 , reinterpret
12 , reinterpret
13 , reinterpretM
13 , reinterpretM
15− -- ** Local operations
15+ -- ** Handling local 'Eff' operations
16 , LocalEnv
16 , LocalEnv
18− -- *** 'Eff'
18+ -- *** Unlifts
19 , localSeqUnlift
19 , localSeqUnlift
23 , localSeqUnliftIO
20 , localSeqUnliftIO
24 , localUnliftIO
22 , localUnliftIO
28+ -- *** Bidirectional lifts
25 , localLiftUnliftIO
30 , localLiftUnliftIO
29import Control.Exception
33import Control.Exception
34+import Control.Monad.IO.Unlift
30import GHC.Stack
35import GHC.Stack
32import Effectful.Internal.Effect
37import Effectful.Internal.Effect
@@ -64,7 +69,7 @@ runInterpreter es0 e m = do
64----------------------------------------
69----------------------------------------
65-- Sending commands
70-- Sending commands
67−-- | Send an operation of a given effect to the interpreter.
72+-- | Send an operation of a given effect to the interpreter for execution.
68send :: (HasCallStack, e :> es) => e (Eff es) a -> Eff es a
73send :: (HasCallStack, e :> es) => e (Eff es) a -> Eff es a
69send op = unsafeEff $ \es -> do
74send op = unsafeEff $ \es -> do
70 Interpreter{..} <- getEnv es
75 Interpreter{..} <- getEnv es
@@ -129,7 +134,7 @@ reinterpretM runHandlerEs interpreter m = unsafeEff $ \es -> do
129 runInterpreter es (Interpreter les interpreter) m
134 runInterpreter es (Interpreter les interpreter) m
131----------------------------------------
136----------------------------------------
132−-- Unlifting
137+-- Unlifts
134-- | Create a local unlifting function with the 'SeqUnlift' strategy. For the
139-- | Create a local unlifting function with the 'SeqUnlift' strategy. For the
135-- general version see 'localUnlift'.
140-- general version see 'localUnlift'.
@@ -139,8 +144,7 @@ localSeqUnlift
139 -> ((forall r. Eff localEs r -> Eff es r) -> Eff es a)
144 -> ((forall r. Eff localEs r -> Eff es r) -> Eff es a)
140 -- ^ Continuation with the unlifting function in scope.
145 -- ^ Continuation with the unlifting function in scope.
141 -> Eff es a
146 -> Eff es a
142−localSeqUnlift (LocalEnv les) k = unsafeEff $ \es -> do
147+localSeqUnlift env = localUnlift env SeqUnlift
143− unsafeSeqUnliftEff les $ \unlift -> unEff (k $ unsafeEff_ . unlift) es
145-- | Create a local unlifting function with the 'SeqUnlift' strategy. For the
149-- | Create a local unlifting function with the 'SeqUnlift' strategy. For the
146-- general version see 'localUnliftIO'.
150-- general version see 'localUnliftIO'.
@@ -151,7 +155,7 @@ localSeqUnliftIO
151 -> ((forall r. Eff localEs r -> IO r) -> IO a)
155 -> ((forall r. Eff localEs r -> IO r) -> IO a)
152 -- ^ Continuation with the unlifting function in scope.
156 -- ^ Continuation with the unlifting function in scope.
153 -> Eff es a
157 -> Eff es a
154−localSeqUnliftIO (LocalEnv les) k = unsafeEff_ $ unsafeSeqUnliftEff les k
158+localSeqUnliftIO env = localUnliftIO env SeqUnlift
156-- | Create a local unlifting function with the given strategy.
160-- | Create a local unlifting function with the given strategy.
157localUnlift
161localUnlift
@@ -161,11 +165,13 @@ localUnlift
161 -> ((forall r. Eff localEs r -> Eff es r) -> Eff es a)
165 -> ((forall r. Eff localEs r -> Eff es r) -> Eff es a)
162 -- ^ Continuation with the unlifting function in scope.
166 -- ^ Continuation with the unlifting function in scope.
163 -> Eff es a
167 -> Eff es a
164−localUnlift (LocalEnv les) strategy k = unsafeEff $ \es -> case strategy of
168+localUnlift (LocalEnv les) strategy k = case strategy of
169+ SeqUnlift -> unsafeEff $ \es -> do
165− SeqUnlift -> unsafeSeqUnliftEff les $ \unlift -> do
170+ unsafeSeqUnliftEff les $ \unlift -> do
166− unEff (k $ unsafeEff_ . unlift) es
171+ (`unEff` es) $ k $ unsafeEff_ . unlift
172+ ConcUnlift p l -> unsafeEff $ \es -> do
167− ConcUnlift p l -> unsafeConcUnliftEff les p l $ \unlift -> do
173+ unsafeConcUnliftEff les p l $ \unlift -> do
168− unEff (k $ unsafeEff_ . unlift) es
174+ (`unEff` es) $ k $ unsafeEff_ . unlift
170-- | Create a local unlifting function with the given strategy.
176-- | Create a local unlifting function with the given strategy.
171localUnliftIO
177localUnliftIO
@@ -176,29 +182,28 @@ localUnliftIO
176 -> ((forall r. Eff localEs r -> IO r) -> IO a)
182 -> ((forall r. Eff localEs r -> IO r) -> IO a)
177 -- ^ Continuation with the unlifting function in scope.
183 -- ^ Continuation with the unlifting function in scope.
178 -> Eff es a
184 -> Eff es a
179−localUnliftIO (LocalEnv les) unlift k = unsafeEff_ $ case unlift of
185+localUnliftIO (LocalEnv les) strategy k = case strategy of
180− SeqUnlift -> unsafeSeqUnliftEff les k
186+ SeqUnlift -> liftIO $ unsafeSeqUnliftEff les k
181− ConcUnlift p l -> unsafeConcUnliftEff les p l k
187+ ConcUnlift p l -> liftIO $ unsafeConcUnliftEff les p l k
183−-- | Create a local unlifting function with the given strategy along with an
189+-- | Utility for lifting 'Eff' operations of type
184−-- unrestricted lifting function.
186−-- Useful for lifting 'IO' operations where the monadic action shows in both
191+-- @'Eff' es a -> 'Eff' es b@
187−-- positive (as a result) and negative (as an argument) position.
189−-- /Note:/ depending on the operations you're lifting, 'localUnliftIO' along
193+-- to
190−-- with 'withLiftSeqOp' might be enough and is more efficient.
194+--
191−localLiftUnliftIO
195+-- @forall localEs. 'Eff' localEs a -> 'Eff' localEs b@
197+-- /Note:/ the operation must not run its argument in a separate thread,
198+-- attempting to do so will result in a runtime error.
192 :: IOE :> es
200 :: IOE :> es
193− => LocalEnv localEs
201+ => ((forall a b localEs. (Eff es a -> Eff es b) -> Eff localEs a -> Eff localEs b) -> Eff es r)
194− -- ^ Local environment.
196− -> ((forall r. IO r -> Eff localEs r) -> (forall r. Eff localEs r -> IO r) -> IO a)
197− -- ^ Continuation with the lifting and unlifting function in scope.
202+ -- ^ Continuation with the lifting function in scope.
198− -> Eff es a
203+ -> Eff es r
199−localLiftUnliftIO (LocalEnv les) unlift k = unsafeEff_ $ case unlift of
204+withLiftMap k = unsafeWithLiftMapIO $ \liftMap -> do
200− SeqUnlift -> unsafeSeqUnliftEff les $ k unsafeEff_
205+ unsafeEff $ \es -> (`unEff` es) $ k $ \f -> do
201− ConcUnlift p l -> unsafeConcUnliftEff les p l $ k unsafeEff_
206+ liftMap $ (`unEff` es) . f . liftIO
203-- | Utility for lifting 'IO' operations of type
208-- | Utility for lifting 'IO' operations of type
@@ -208,7 +213,7 @@ localLiftUnliftIO (LocalEnv les) unlift k = unsafeEff_ $ case unlift of
209-- @forall localEs. 'Eff' localEs a -> 'Eff' localEs b@
214-- @forall localEs. 'Eff' localEs a -> 'Eff' localEs b@
211−-- /Note:/ the 'IO' operation must not run its argument in a separate thread,
216+-- /Note:/ the operation must not run its argument in a separate thread,
212-- attempting to do so will result in a runtime error.
217-- attempting to do so will result in a runtime error.
214-- Useful e.g. for lifting the unmasking function in
219-- Useful e.g. for lifting the unmasking function in
@@ -221,16 +226,64 @@ localLiftUnliftIO (LocalEnv les) unlift k = unsafeEff_ $ case unlift of
223-- runFork :: IOE :> es => Eff (Fork : es) a -> Eff es a
228-- runFork :: IOE :> es => Eff (Fork : es) a -> Eff es a
224−-- runFork = interpretM $ \env (ForkWithUnmask m) -> withLiftSeqOp $ \liftSeqOp -> do
229+-- runFork = interpretM $ \env (ForkWithUnmask m) -> withLiftMapIO $ \liftMap -> do
225-- localUnliftIO env (ConcUnlift Ephemeral $ Limited 1) $ \unlift -> do
230-- localUnliftIO env (ConcUnlift Ephemeral $ Limited 1) $ \unlift -> do
226−-- forkIOWithUnmask $ \unmask -> unlift $ m $ liftSeqOp unmask
231+-- forkIOWithUnmask $ \unmask -> unlift $ m $ liftMap unmask
228−withLiftSeqOp
233+withLiftMapIO
229 :: IOE :> es
234 :: IOE :> es
230 => ((forall a b localEs. (IO a -> IO b) -> Eff localEs a -> Eff localEs b) -> Eff es r)
235 => ((forall a b localEs. (IO a -> IO b) -> Eff localEs a -> Eff localEs b) -> Eff es r)
231 -- ^ Continuation with the lifting function in scope.
236 -- ^ Continuation with the lifting function in scope.
232 -> Eff es r
237 -> Eff es r
233−withLiftSeqOp = unsafeWithLiftSeqOp
238+withLiftMapIO = unsafeWithLiftMapIO
240+----------------------------------------
241+-- Bidirectional lifts
243+-- | Create a local lifting and unlifting function with the given strategy.
245+-- Useful for lifting complicated 'Eff' operations where the monadic action
246+-- shows in both positive (as a result) and negative (as an argument) position.
248+-- /Note:/ depending on the operation you're lifting 'localUnlift' along with
249+-- 'withLiftMap' might be enough and is more efficient.
253+ -- ^ Local environment.
255+ -> ((forall r. Eff es r -> Eff localEs r) -> (forall r. Eff localEs r -> Eff es r) -> Eff es a)
256+ -- ^ Continuation with the lifting and unlifting function in scope.
258+localLiftUnlift (LocalEnv les) strategy k = case strategy of
259+ SeqUnlift -> unsafeEff $ \es -> do
260+ unsafeSeqUnliftEff es $ \unliftEs -> do
261+ unsafeSeqUnliftEff les $ \unliftLocalEs -> do
262+ (`unEff` es) $ k (unsafeEff_ . unliftEs) (liftIO . unliftLocalEs)
263+ ConcUnlift p l -> unsafeEff $ \es -> do
264+ unsafeConcUnliftEff es p l $ \unliftEs -> do
265+ unsafeConcUnliftEff les p l $ \unliftLocalEs -> do
266+ (`unEff` es) $ k (unsafeEff_ . unliftEs) (liftIO . unliftLocalEs)
268+-- | Create a local unlifting function with the given strategy along with an
269+-- unrestricted lifting function.
271+-- Useful for lifting complicated 'IO' operations where the monadic action shows
272+-- in both positive (as a result) and negative (as an argument) position.
274+-- /Note:/ depending on the operation you're lifting 'localUnliftIO' along with
275+-- 'withLiftMapIO' might be enough and is more efficient.
279+ -- ^ Local environment.
281+ -> ((forall r. IO r -> Eff localEs r) -> (forall r. Eff localEs r -> IO r) -> IO a)
282+ -- ^ Continuation with the lifting and unlifting function in scope.
284+localLiftUnliftIO (LocalEnv les) strategy k = case strategy of
285+ SeqUnlift -> liftIO $ unsafeSeqUnliftEff les $ k unsafeEff_
286+ ConcUnlift p l -> liftIO $ unsafeConcUnliftEff les p l $ k unsafeEff_
236-- >>> import Control.Concurrent
289-- >>> import Control.Concurrent