[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