From 721b0f13cb6898a89d860d9f259b324699c4a13a Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Sun, 2 Aug 2026 04:25:36 +0200 Subject: [PATCH] Document interaction of Error, NonDet and ReturnWith with threads --- effectful-core/src/Effectful/Error/Dynamic.hs | 3 +++ effectful-core/src/Effectful/Error/Static.hs | 19 +++++++++++++++++++ effectful-core/src/Effectful/NonDet.hs | 4 ++++ .../src/Effectful/ReturnWith/Dynamic.hs | 3 +++ .../src/Effectful/ReturnWith/Static.hs | 19 +++++++++++++++++++ 5 files changed, 48 insertions(+) diff --git a/effectful-core/src/Effectful/Error/Dynamic.hs b/effectful-core/src/Effectful/Error/Dynamic.hs index 97c08d6a..599adbd6 100644 --- a/effectful-core/src/Effectful/Error/Dynamic.hs +++ b/effectful-core/src/Effectful/Error/Dynamic.hs @@ -4,6 +4,9 @@ -- t'Control.Monad.Except.MonadError' instance for compatibility with existing -- code, it's recommended to use the statically dispatched variant, -- i.e. "Effectful.Error.Static". +-- +-- All caveats described in "Effectful.Error.Static" (in particular the +-- interaction with threads) apply. module Effectful.Error.Dynamic ( -- * Effect Error(..) diff --git a/effectful-core/src/Effectful/Error/Static.hs b/effectful-core/src/Effectful/Error/Static.hs index af50e015..d6dbe337 100644 --- a/effectful-core/src/Effectful/Error/Static.hs +++ b/effectful-core/src/Effectful/Error/Static.hs @@ -75,6 +75,20 @@ -- /Hint:/ if you'd like to reproduce the transactional behavior with the -- t'Effectful.State.Static.Local.State' effect, appropriate usage of -- 'Effectful.Exception.bracketOnError' will do the trick. +-- +-- === Interaction with threads +-- +-- The 'Error' effect uses runtime exceptions underneath, so the usual rules +-- apply. In particular, in multi-threaded code an error thrown in a child +-- thread will not automatically propagate to the parent. If you need that, use +-- functions such as @withAsync@ from the +-- [Effectful.Concurrent.Async](https://hackage.haskell.org/package/effectful/docs/Effectful-Concurrent-Async.html) +-- module of the @effectful@ package (which propagate exceptions from child +-- threads to their parents) or arrange the propagation yourself. +-- +-- For more information see the documentation of the +-- [Concurrent](https://hackage.haskell.org/package/effectful/docs/Effectful-Concurrent.html#t:Concurrent) +-- effect. module Effectful.Error.Static ( -- * Effect Error @@ -297,6 +311,11 @@ instance Show ErrorWrapper where . (errRep ++) . ("\n" ++) . (prettyCallStack cs ++) + . ("\n\nIf you see this message, most likely an error escaped the " ++) + . ("scope of its handler, e.g. by being thrown from a thread that " ++) + . ("outlived it, or was caught by an overly zealous exception handler. " ++) + . ("For more information see the documentation of the " ++) + . ("Effectful.Error.Static module." ++) instance Exception ErrorWrapper where -- See discussion in https://github.com/haskell-effectful/effectful/pull/232. diff --git a/effectful-core/src/Effectful/NonDet.hs b/effectful-core/src/Effectful/NonDet.hs index f40cce2c..6449ca9a 100644 --- a/effectful-core/src/Effectful/NonDet.hs +++ b/effectful-core/src/Effectful/NonDet.hs @@ -1,5 +1,9 @@ -- | Provider of the t'Control.Applicative.Alternative' and -- t'Control.Monad.MonadPlus' instance for 'Eff'. +-- +-- /Note:/ the 'NonDet' effect uses the t'Effectful.Error.Static.Error' effect +-- underneath, so caveats described in "Effectful.Error.Static" (in particular +-- the interaction with threads) apply. module Effectful.NonDet ( -- * Effect NonDet(..) diff --git a/effectful-core/src/Effectful/ReturnWith/Dynamic.hs b/effectful-core/src/Effectful/ReturnWith/Dynamic.hs index 0b623da9..22dbb6b3 100644 --- a/effectful-core/src/Effectful/ReturnWith/Dynamic.hs +++ b/effectful-core/src/Effectful/ReturnWith/Dynamic.hs @@ -4,6 +4,9 @@ -- recommended to use the statically dispatched variant, -- i.e. "Effectful.ReturnWith.Static". -- +-- All caveats described in "Effectful.ReturnWith.Static" (in particular the +-- interaction with threads) apply. +-- -- @since 2.7.0.0 module Effectful.ReturnWith.Dynamic ( -- * Effect diff --git a/effectful-core/src/Effectful/ReturnWith/Static.hs b/effectful-core/src/Effectful/ReturnWith/Static.hs index 18eed300..4133b3b3 100644 --- a/effectful-core/src/Effectful/ReturnWith/Static.hs +++ b/effectful-core/src/Effectful/ReturnWith/Static.hs @@ -16,6 +16,20 @@ -- >>> runEff . runReturnWith $ classify (-5) -- "negative" -- +-- === Interaction with threads +-- +-- The 'ReturnWith' effect uses runtime exceptions underneath, so the usual +-- rules apply. In particular, in multi-threaded code a call to 'returnWith' in +-- a child thread will not automatically propagate to the parent. If you need +-- that, use functions such as @withAsync@ from the +-- [Effectful.Concurrent.Async](https://hackage.haskell.org/package/effectful/docs/Effectful-Concurrent-Async.html) +-- module of the @effectful@ package (which propagate exceptions from child +-- threads to their parents) or arrange the propagation yourself. +-- +-- For more information see the documentation of the +-- [Concurrent](https://hackage.haskell.org/package/effectful/docs/Effectful-Concurrent.html#t:Concurrent) +-- effect. +-- -- @since 2.7.0.0 module Effectful.ReturnWith.Static ( -- * Effect @@ -81,6 +95,11 @@ instance Show ReturnWithWrapper where = showParen (p > 10) $ ("Effectful.ReturnWith.Static.ReturnWithWrapper\n" ++) . (prettyCallStack cs ++) + . ("\n\nIf you see this message, most likely a call to returnWith " ++) + . ("escaped the scope of its handler, e.g. by being made from a thread " ++) + . ("that outlived it, or was caught by an overly zealous exception " ++) + . ("handler. For more information see the documentation of the " ++) + . ("Effectful.ReturnWith.Static module." ++) instance Exception ReturnWithWrapper where -- See discussion in https://github.com/haskell-effectful/effectful/pull/232.