@@ -47,6 +47,8 @@ module Effectful.Internal.Monad
4747 , withSeqEffToIO
4848 , withEffToIO
4949 , withConcEffToIO
50+ , reallyUnsafeLiftMapIO
51+ , reallyUnsafeUnliftIO
5052
5153 -- ** Low-level unlifts
5254 , seqUnliftIO
@@ -261,6 +263,53 @@ concUnliftIO es Ephemeral Unlimited = ephemeralConcUnlift es maxBound
261263concUnliftIO es Persistent (Limited threads) = persistentConcUnlift es False threads
262264concUnliftIO es Persistent Unlimited = persistentConcUnlift es True maxBound
263265
266+ -- | Utility for lifting 'IO' computations of type
267+ --
268+ -- @'IO' a -> 'IO' b@
269+ --
270+ -- to
271+ --
272+ -- @'Eff' es a -> 'Eff' es b@
273+ --
274+ -- This function is __really unsafe__ because:
275+ --
276+ -- - It can be used to introduce arbitrary 'IO' actions into pure 'Eff'
277+ -- computations.
278+ --
279+ -- - The 'IO' computation must run its argument in a way that's perceived as
280+ -- sequential to the outside observer, e.g. in the same thread or in a worker
281+ -- thread that finishes before the argument is run again.
282+ --
283+ -- __Warning:__ if you disregard the second point, you will experience weird
284+ -- bugs, data races or internal consistency check failures.
285+ --
286+ -- When in doubt, use 'Effectful.Dispatch.Static.unsafeLiftMapIO', especially
287+ -- since this version saves only a simple safety check per call of
288+ -- @reallyUnsafeLiftMapIO f@.
289+ reallyUnsafeLiftMapIO :: (IO a -> IO b ) -> Eff es a -> Eff es b
290+ reallyUnsafeLiftMapIO f m = unsafeEff $ \ es -> f (unEff m es)
291+
292+ -- | Create an unlifting function.
293+ --
294+ -- This function is __really unsafe__ because:
295+ --
296+ -- - It can be used to introduce arbitrary 'IO' actions into pure 'Eff'
297+ -- computations.
298+ --
299+ -- - Unlifted 'Eff' computations must be run in a way that's perceived as
300+ -- sequential to the outside observer, e.g. in the same thread as the caller
301+ -- of 'reallyUnsafeUnliftIO' or in a worker thread that finishes before
302+ -- another unlifted computation is run.
303+ --
304+ -- __Warning:__ if you disregard the second point, you will experience weird
305+ -- bugs, data races or internal consistency check failures.
306+ --
307+ -- When in doubt, use 'Effectful.Dispatch.Static.unsafeSeqUnliftIO', especially
308+ -- since this version saves only a simple safety check per call of the unlifting
309+ -- function.
310+ reallyUnsafeUnliftIO :: ((forall r . Eff es r -> IO r ) -> IO a ) -> Eff es a
311+ reallyUnsafeUnliftIO k = unsafeEff $ \ es -> k (`unEff` es)
312+
264313----------------------------------------
265314-- Base
266315
@@ -308,39 +357,40 @@ instance NonDet :> es => MonadPlus (Eff es)
308357-- Exception
309358
310359instance C. MonadThrow (Eff es ) where
311- throwM = unsafeEff_ . E. throwIO
360+ throwM = unsafeEff_ . withFrozenCallStack E. throwIO
312361
313362instance C. MonadCatch (Eff es ) where
314- catch m handler = unsafeEff $ \ es -> do
315- unEff m es `E.catch` \ e -> do
316- unEff (handler e) es
363+ catch action handler = reallyUnsafeUnliftIO $ \ unlift -> do
364+ E. catch (unlift action) (unlift . handler)
317365
318366instance C. MonadMask (Eff es ) where
319- mask k = unsafeEff $ \ es -> E. mask $ \ unmask ->
320- unEff (k $ \ m -> unsafeEff $ unmask . unEff m) es
367+ mask k = reallyUnsafeUnliftIO $ \ unlift -> do
368+ E. mask $ \ release -> unlift $ k (reallyUnsafeLiftMapIO release)
321369
322- uninterruptibleMask k = unsafeEff $ \ es -> E. uninterruptibleMask $ \ unmask ->
323- unEff (k $ \ m -> unsafeEff $ unmask . unEff m) es
370+ uninterruptibleMask k = reallyUnsafeUnliftIO $ \ unlift -> do
371+ E. uninterruptibleMask $ \ release -> unlift $ k (reallyUnsafeLiftMapIO release)
324372
325- generalBracket acquire release use = unsafeEff $ \ es -> E. mask $ \ unmask -> do
326- resource <- unEff acquire es
373+ generalBracket before after action = reallyUnsafeUnliftIO $ \ unlift -> do
374+ E. mask $ \ unmask -> do
375+ a <- unlift before
327376#if MIN_VERSION_base(4,21,0)
328- b <- E. catchNoPropagate
329- (unmask (unEff (use resource) es))
330- (\ ec@ (E. ExceptionWithContext _ e) -> do
331- _ <- unEff (release resource $ C. ExitCaseException e) es
332- E. rethrowIO ec
333- )
377+ b <- E. catchNoPropagate
378+ (unmask . unlift $ action a)
379+ (\ ec@ (E. ExceptionWithContext _ e) -> do
380+ _ <- E. annotateIO (E. WhileHandling (E. toException ec)) $ do
381+ unlift . after a $ C. ExitCaseException e
382+ E. rethrowIO ec
383+ )
334384#else
335- b <- E. catch
336- (unmask (unEff (use resource) es) )
337- (\ e -> do
338- _ <- unEff (release resource $ C. ExitCaseException e) es
339- E. throwIO e
340- )
385+ b <- E. catch
386+ (unmask . unlift $ action a )
387+ (\ e -> do
388+ _ <- unlift . after a $ C. ExitCaseException e
389+ E. throwIO e
390+ )
341391#endif
342- c <- unEff (release resource $ C. ExitCaseSuccess b) es
343- pure (b, c)
392+ c <- unlift . after a $ C. ExitCaseSuccess b
393+ pure (b, c)
344394
345395----------------------------------------
346396-- Fail
0 commit comments