@@ -1,5 +1,5 @@
11module Effectful.Interpreter
2− ( -- * Sending commands
2+ ( -- * Sending operations to the handler
33 send
44
55 -- * Handling effects
@@ -12,21 +12,26 @@ module Effectful.Interpreter
1212 , reinterpret
1313 , reinterpretM
1414
15− -- ** Local operations
15+ -- ** Handling local 'Eff' operations
1616 , LocalEnv
1717
18− -- *** 'Eff'
18+ -- *** Unlifts
1919 , localSeqUnlift
20− , localUnlift
21−
22− -- *** 'IO'
2320 , localSeqUnliftIO
21+ , localUnlift
2422 , localUnliftIO
23+
24+ -- *** Lifts
25+ , withLiftMap
26+ , withLiftMapIO
27+
28+ -- *** Bidirectional lifts
29+ , localLiftUnlift
2530 , localLiftUnliftIO
26− , withLiftSeqOp
2731 ) where
2832
2933import Control.Exception
34+import Control.Monad.IO.Unlift
3035import GHC.Stack
3136
3237import Effectful.Internal.Effect
@@ -64,7 +69,7 @@ runInterpreter es0 e m = do
6469----------------------------------------
6570-- Sending commands
6671
67−-- | Send an operation of a given effect to the interpreter.
72+-- | Send an operation of a given effect to the interpreter for execution.
6873send :: (HasCallStack, e :> es) => e (Eff es) a -> Eff es a
6974send op = unsafeEff $ \es -> do
7075 Interpreter{..} <- getEnv es
@@ -129,7 +134,7 @@ reinterpretM runHandlerEs interpreter m = unsafeEff $ \es -> do
129134 runInterpreter es (Interpreter les interpreter) m
130135
131136----------------------------------------
132−-- Unlifting
137+-- Unlifts
133138
134139-- | Create a local unlifting function with the 'SeqUnlift' strategy. For the
135140-- general version see 'localUnlift'.
@@ -139,8 +144,7 @@ localSeqUnlift
139144 -> ((forall r. Eff localEs r -> Eff es r) -> Eff es a)
140145 -- ^ Continuation with the unlifting function in scope.
141146 -> Eff es a
142−localSeqUnlift (LocalEnv les) k = unsafeEff $ \es -> do
143− unsafeSeqUnliftEff les $ \unlift -> unEff (k $ unsafeEff_ . unlift) es
147+localSeqUnlift env = localUnlift env SeqUnlift
144148
145149-- | Create a local unlifting function with the 'SeqUnlift' strategy. For the
146150-- general version see 'localUnliftIO'.
@@ -151,7 +155,7 @@ localSeqUnliftIO
151155 -> ((forall r. Eff localEs r -> IO r) -> IO a)
152156 -- ^ Continuation with the unlifting function in scope.
153157 -> Eff es a
154−localSeqUnliftIO (LocalEnv les) k = unsafeEff_ $ unsafeSeqUnliftEff les k
158+localSeqUnliftIO env = localUnliftIO env SeqUnlift
155159
156160-- | Create a local unlifting function with the given strategy.
157161localUnlift
@@ -161,11 +165,13 @@ localUnlift
161165 -> ((forall r. Eff localEs r -> Eff es r) -> Eff es a)
162166 -- ^ Continuation with the unlifting function in scope.
163167 -> Eff es a
164−localUnlift (LocalEnv les) strategy k = unsafeEff $ \es -> case strategy of
165− SeqUnlift -> unsafeSeqUnliftEff les $ \unlift -> do
166− unEff (k $ unsafeEff_ . unlift) es
167− ConcUnlift p l -> unsafeConcUnliftEff les p l $ \unlift -> do
168− unEff (k $ unsafeEff_ . unlift) es
168+localUnlift (LocalEnv les) strategy k = case strategy of
169+ SeqUnlift -> unsafeEff $ \es -> do
170+ unsafeSeqUnliftEff les $ \unlift -> do
171+ (`unEff` es) $ k $ unsafeEff_ . unlift
172+ ConcUnlift p l -> unsafeEff $ \es -> do
173+ unsafeConcUnliftEff les p l $ \unlift -> do
174+ (`unEff` es) $ k $ unsafeEff_ . unlift
169175
170176-- | Create a local unlifting function with the given strategy.
171177localUnliftIO
@@ -176,29 +182,28 @@ localUnliftIO
176182 -> ((forall r. Eff localEs r -> IO r) -> IO a)
177183 -- ^ Continuation with the unlifting function in scope.
178184 -> Eff es a
179−localUnliftIO (LocalEnv les) unlift k = unsafeEff_ $ case unlift of
180− SeqUnlift -> unsafeSeqUnliftEff les k
181− ConcUnlift p l -> unsafeConcUnliftEff les p l k
185+localUnliftIO (LocalEnv les) strategy k = case strategy of
186+ SeqUnlift -> liftIO $ unsafeSeqUnliftEff les k
187+ ConcUnlift p l -> liftIO $ unsafeConcUnliftEff les p l k
182188
183−-- | Create a local unlifting function with the given strategy along with an
184−-- unrestricted lifting function.
189+-- | Utility for lifting 'Eff' operations of type
185190--
186−-- Useful for lifting 'IO' operations where the monadic action shows in both
187−-- positive (as a result) and negative (as an argument) position.
191+-- @'Eff' es a -> 'Eff' es b@
188192--
189−-- /Note:/ depending on the operations you're lifting, 'localUnliftIO' along
190−-- with 'withLiftSeqOp' might be enough and is more efficient.
191−localLiftUnliftIO
193+-- to
194+--
195+-- @forall localEs. 'Eff' localEs a -> 'Eff' localEs b@
196+--
197+-- /Note:/ the operation must not run its argument in a separate thread,
198+-- attempting to do so will result in a runtime error.
199+withLiftMap
192200 :: IOE :> es
193− => LocalEnv localEs
194− -- ^ Local environment.
195− -> UnliftStrategy
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.
198− -> Eff es a
199−localLiftUnliftIO (LocalEnv les) unlift k = unsafeEff_ $ case unlift of
200− SeqUnlift -> unsafeSeqUnliftEff les $ k unsafeEff_
201− ConcUnlift p l -> unsafeConcUnliftEff les p l $ k unsafeEff_
201+ => ((forall a b localEs. (Eff es a -> Eff es b) -> Eff localEs a -> Eff localEs b) -> Eff es r)
202+ -- ^ Continuation with the lifting function in scope.
203+ -> Eff es r
204+withLiftMap k = unsafeWithLiftMapIO $ \liftMap -> do
205+ unsafeEff $ \es -> (`unEff` es) $ k $ \f -> do
206+ liftMap $ (`unEff` es) . f . liftIO
202207
203208-- | Utility for lifting 'IO' operations of type
204209--
@@ -208,7 +213,7 @@ localLiftUnliftIO (LocalEnv les) unlift k = unsafeEff_ $ case unlift of
208213--
209214-- @forall localEs. 'Eff' localEs a -> 'Eff' localEs b@
210215--
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,
212217-- attempting to do so will result in a runtime error.
213218--
214219-- Useful e.g. for lifting the unmasking function in
@@ -221,16 +226,64 @@ localLiftUnliftIO (LocalEnv les) unlift k = unsafeEff_ $ case unlift of
221226--
222227-- >>> :{
223228-- 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
225230-- localUnliftIO env (ConcUnlift Ephemeral $ Limited 1) $ \unlift -> do
226−-- forkIOWithUnmask $ \unmask -> unlift $ m $ liftSeqOp unmask
231+-- forkIOWithUnmask $ \unmask -> unlift $ m $ liftMap unmask
227232-- :}
228−withLiftSeqOp
233+withLiftMapIO
229234 :: IOE :> es
230235 => ((forall a b localEs. (IO a -> IO b) -> Eff localEs a -> Eff localEs b) -> Eff es r)
231236 -- ^ Continuation with the lifting function in scope.
232237 -> Eff es r
233−withLiftSeqOp = unsafeWithLiftSeqOp
238+withLiftMapIO = unsafeWithLiftMapIO
239+
240+----------------------------------------
241+-- Bidirectional lifts
242+
243+-- | Create a local lifting and unlifting function with the given strategy.
244+--
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.
247+--
248+-- /Note:/ depending on the operation you're lifting 'localUnlift' along with
249+-- 'withLiftMap' might be enough and is more efficient.
250+localLiftUnlift
251+ :: IOE :> es
252+ => LocalEnv localEs
253+ -- ^ Local environment.
254+ -> UnliftStrategy
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.
257+ -> Eff es a
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)
267+
268+-- | Create a local unlifting function with the given strategy along with an
269+-- unrestricted lifting function.
270+--
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.
273+--
274+-- /Note:/ depending on the operation you're lifting 'localUnliftIO' along with
275+-- 'withLiftMapIO' might be enough and is more efficient.
276+localLiftUnliftIO
277+ :: IOE :> es
278+ => LocalEnv localEs
279+ -- ^ Local environment.
280+ -> UnliftStrategy
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.
283+ -> Eff es a
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_
234287
235288-- $setup
236289-- >>> import Control.Concurrent