@@ -21,9 +21,9 @@ module Effectful.Interpreter
2121
2222 -- *** 'IO'
2323 , localSeqUnliftIO
24− , localSeqLiftUnliftIO
2524 , localUnliftIO
2625 , localLiftUnliftIO
26+ , withLiftSeqOp
2727 ) where
2828
2929import Control.Exception
@@ -140,7 +140,7 @@ localSeqUnlift
140140 -- ^ Continuation with the unlifting function in scope.
141141 -> Eff es a
142142localSeqUnlift (LocalEnv les) f = unsafeEff $ \es -> do
143− unEff (seqUnliftEff $ \k -> unEff (f $ unsafeEff_ . k) es) les
143+ unEff (unsafeSeqUnliftEff $ \unlift -> unEff (f $ unsafeEff_ . unlift) es) les
144144
145145-- | Create a local unlifting function with the 'SeqUnlift' strategy. For the
146146-- general version see 'localUnliftIO'.
@@ -151,22 +151,7 @@ localSeqUnliftIO
151151 -> ((forall r. Eff localEs r -> IO r) -> IO a)
152152 -- ^ Continuation with the unlifting function in scope.
153153 -> Eff es a
154−localSeqUnliftIO (LocalEnv les) f = unsafeEff_ $ unEff (seqUnliftEff f) les
155−
156−-- | Create a local lifting and unlifting function with the 'SeqUnlift'
157−-- strategy. For the general version see 'localLiftUnliftIO'.
158−--
159−-- /Note:/ the strategy applies only to the unlifting function, lifting function
160−-- doesn't have any restrictions.
161−localSeqLiftUnliftIO
162− :: IOE :> es
163− => LocalEnv localEs
164− -- ^ Local environment.
165− -> ((forall r. IO r -> Eff localEs r) -> (forall r. Eff localEs r -> IO r) -> IO a)
166− -- ^ Continuation with the lifting and unlifting function.
167− -> Eff es a
168−localSeqLiftUnliftIO (LocalEnv les) f = unsafeEff_ $ do
169− unEff (seqUnliftEff $ f unsafeEff_) les
154+localSeqUnliftIO (LocalEnv les) f = unsafeEff_ $ unEff (unsafeSeqUnliftEff f) les
170155
171156-- | Create a local unlifting function with the given strategy.
172157localUnlift
@@ -176,9 +161,11 @@ localUnlift
176161 -> ((forall r. Eff localEs r -> Eff es r) -> Eff es a)
177162 -- ^ Continuation with the unlifting function in scope.
178163 -> Eff es a
179−localUnlift (LocalEnv les) unlift f = unsafeEff $ \es -> case unlift of
180− SeqUnlift -> unEff (seqUnliftEff $ \k -> unEff (f $ unsafeEff_ . k) es) les
181− ConcUnlift p b -> unEff (concUnliftEff p b $ \k -> unEff (f $ unsafeEff_ . k) es) les
164+localUnlift (LocalEnv les) strategy f = unsafeEff $ \es -> case strategy of
165+ SeqUnlift ->
166+ unEff (unsafeSeqUnliftEff $ \unlift -> unEff (f $ unsafeEff_ . unlift) es) les
167+ ConcUnlift p l ->
168+ unEff (unsafeConcUnliftEff p l $ \unlift -> unEff (f $ unsafeEff_ . unlift) es) les
182169
183170-- | Create a local unlifting function with the given strategy.
184171localUnliftIO
@@ -190,15 +177,42 @@ localUnliftIO
190177 -- ^ Continuation with the unlifting function in scope.
191178 -> Eff es a
192179localUnliftIO (LocalEnv les) unlift f = unsafeEff_ $ case unlift of
193− SeqUnlift -> unEff (seqUnliftEff f) les
194− ConcUnlift p b -> unEff (concUnliftEff p b f) les
180+ SeqUnlift -> unEff (unsafeSeqUnliftEff f) les
181+ ConcUnlift p l -> unEff (unsafeConcUnliftEff p l f) les
182+
183+-- | Create a local unlifting function with the given strategy along with an
184+-- unrestricted lifting function.
185+--
186+-- Useful for lifting local 'Eff' operations where the monadic action shows in
187+-- both positive and negative position.
188+--
189+-- /Note:/ depending on the operations you're lifting, 'localUnliftIO' along
190+-- with 'withLiftSeqOp' might be enough and is more efficient.
191+localLiftUnliftIO
192+ :: 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 f = unsafeEff_ $ case unlift of
200+ SeqUnlift -> unEff (unsafeSeqUnliftEff $ f unsafeEff_) les
201+ ConcUnlift p l -> unEff (unsafeConcUnliftEff p l $ f unsafeEff_) les
195202
196−-- | Create a local lifting and unlifting function with the given strategy.
203+-- | Utility for lifting 'IO' actions of type
197204--
198−-- /Note:/ the strategy applies only to the unlifting function, lifting function
199−-- doesn't have any restrictions.
205+-- @'IO' a -> 'IO' b@
200206--
201−-- Necessary for lifting 'Control.Concurrent.forkIOWithUnmask'-like operations:
207+-- to
208+--
209+-- @forall localEs. 'Eff' localEs a -> 'Eff' localEs b@
210+--
211+-- /Note:/ the 'IO' operation must not try to run its argument in a different
212+-- thread, attempting to do so will result in a runtime error.
213+--
214+-- Useful e.g. for lifting the unmasking function in
215+-- 'Control.Exception.mask'-like operations:
202216--
203217-- >>> :{
204218-- data Fork :: Effect where
@@ -207,21 +221,16 @@ localUnliftIO (LocalEnv les) unlift f = unsafeEff_ $ case unlift of
207221--
208222-- >>> :{
209223-- runFork :: IOE :> es => Eff (Fork : es) a -> Eff es a
210−-- runFork = interpretM $ \env (ForkWithUnmask m) -> do
211−-- localLiftUnliftIO env (ConcUnlift Persistent $ Limited 1) $ \lift unlift -> do
212−-- forkIOWithUnmask $ \unmask -> unlift $ m $ lift . unmask . unlift
224+-- runFork = interpretM $ \env (ForkWithUnmask m) -> withLiftSeqOp $ \liftSeqOp -> do
225+-- localUnliftIO env (ConcUnlift Ephemeral $ Limited 1) $ \unlift -> do
226+-- forkIOWithUnmask $ \unmask -> unlift $ m $ liftSeqOp unmask
213227-- :}
214−localLiftUnliftIO
228+withLiftSeqOp
215229 :: IOE :> es
216− => LocalEnv localEs
217− -- ^ Local environment.
218− -> UnliftStrategy
219− -> ((forall r. IO r -> Eff localEs r) -> (forall r. Eff localEs r -> IO r) -> IO a)
220− -- ^ Continuation with the lifting and unlifting function in scope.
221− -> Eff es a
222−localLiftUnliftIO (LocalEnv les) unlift f = unsafeEff_ $ case unlift of
223− SeqUnlift -> unEff (seqUnliftEff $ f unsafeEff_) les
224− ConcUnlift p b -> unEff (concUnliftEff p b $ f unsafeEff_) les
230+ => ((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.
232+ -> Eff es r
233+withLiftSeqOp = unsafeWithLiftSeqOp
225234
226235-- $setup
227236-- >>> import Control.Concurrent