[Haskell-cafe] Best way to implement "scoped exceptions"?
Tom Ellis
tom-lists-haskell-cafe-2023 at jaguarpaw.co.uk
Sun May 25 16:32:13 UTC 2025
On Wed, Dec 28, 2022 at 08:38:04PM +0000, Tom Ellis wrote:
> I'd like to implement "scoped exceptions"[1], that is, a combinator of
> type
>
> withScopedException ::
> ((forall a. e -> IO a) -> IO r) ->
> IO (Either e r)
[...]
> Here's a hacky way of doing it, based on tagging each exception with a
> unique value, and then filtering when handling:
>
> data MyException where
> MyException :: e -> Data.Unique.Unique -> MyException
>
> instance Show MyException where
> show _ = "<MyException>"
>
> instance Exception MyException
>
> withScopedException ::
> ((forall a. e -> IO a) -> IO r) -> IO (Either e r)
> wiathScopedException f = do
> fresh <- Data.Unique.newUnique
> flip tryJust (f (\e -> throwIO (MyException e fresh))) $ \case
> MyException e tag ->
> -- unsafeCoerce is very unpleasant
> if tag == fresh then Just (unsafeCoerce e) else Nothing
>
> This is the approach taken by the effectful library[2]. But is there
> a better way? Can I persuade GHC's RTS to work like this directly?
I found a better way, or more accurately LSLeary suggested it to me,
now implemented in Bluefin.Internal.Exception.Scoped:
https://hackage-content.haskell.org/package/bluefin-internal-0.1.0.0/docs/Bluefin-Internal-Exception-Scoped.html
The innovation is to have a `Key` type that supports:
newKey :: IO (Key a)
eqKey :: forall a b. Key a -> Key b -> Maybe (a :~~: b)
This allows us to tag thrown values with a `Key` and unwrap them only
when we have another copy of the same key, which allows us to obtain
type equality. (`Key` is also available in Apfelmus's `vault`
package.)
Tom
More information about the Haskell-Cafe
mailing list