@@ -12,6 +12,7 @@ module Effectful.Error
13import Control.Exception
13import Control.Exception
14import Data.Typeable
14import Data.Typeable
15import GHC.Stack
16import GHC.Stack
17import Effectful.Internal.Effect
18import Effectful.Internal.Effect
@@ -20,48 +21,68 @@ import Effectful.Internal.Monad
20import Effectful.Internal.Utils
21import Effectful.Internal.Utils
22data Error e :: Effect where
23data Error e :: Effect where
23− Error :: Error e m r
24+ Error :: Unique -> Error e m r
26+-- | TODO: write about possibility of an error escaping the scope of 'runError'
27+-- when misused with 'AsyncE'.
26 :: forall e es a. Typeable e
29 :: forall e es a. Typeable e
27 => Eff (Error e : es) a
30 => Eff (Error e : es) a
28 -> Eff es (Either ([String], e) a)
31 -> Eff es (Either ([String], e) a)
29runError (Eff m) = unsafeEff $ \es0 -> mask $ \release -> do
32runError (Eff m) = unsafeEff $ \es0 -> mask $ \release -> do
33+ -- A unique tag is picked so that different runError handlers for the same
34+ -- type don't catch each other's exceptions.
30 size0 <- sizeEnv es0
36 size0 <- sizeEnv es0
31− es <- unsafeConsEnv (IdE (Error @e)) noRelinker es0
37+ es <- unsafeConsEnv (IdE (Error @e tag)) noRelinker es0
32− try (release $ m es) >>= \case
33− Right a -> Right a <$ unsafeTailEnv size0 es
38+ r <- tryErrorIO tag (release $ m es) `onException` unsafeTailEnv size0 es
34− Left (WrapErr cs e) -> Left (cs, e) <$ unsafeTailEnv size0 es
39+ _ <- unsafeTailEnv size0 es
37− :: (HasCallStack, Typeable e, Error e :> es)
43+ :: forall e es a. (HasCallStack, Typeable e, Error e :> es)
39 -> Eff es a
45 -> Eff es a
40−throwError e = unsafeEff_ $ do
46+throwError e = readerEffectM @(Error e) $ \(IdE (Error tag)) -> unsafeEff_ $ do
41− throwIO $ WrapErr (ppCallStack <$> getCallStack callStack) e
47+ throwIO $ WrapErr tag (ppCallStack <$> getCallStack callStack) e
44− :: (Typeable e, Error e :> es)
50+ :: forall e es a. (Typeable e, Error e :> es)
45 => Eff es a
51 => Eff es a
46 -> ([String] -> e -> Eff es a)
52 -> ([String] -> e -> Eff es a)
47 -> Eff es a
53 -> Eff es a
48−catchError (Eff m) handler = unsafeEff $ \es -> do
54+catchError (Eff m) handler = do
55+ readerEffectM @(Error e) $ \(IdE (Error tag)) -> unsafeEff $ \es -> do
49− size <- sizeEnv es
56+ size <- sizeEnv es
50− m es `catch` \(WrapErr e cs) -> do
57+ catchErrorIO tag (m es) $ \cs e -> do
51− checkSizeEnv size es
58+ checkSizeEnv size es
52− unEff (handler e cs) es
59+ unEff (handler cs e) es
55− :: (Typeable e, Error e :> es)
62+ :: forall e es a. (Typeable e, Error e :> es)
56 => Eff es a
63 => Eff es a
57 -> Eff es (Either ([String], e) a)
64 -> Eff es (Either ([String], e) a)
58−tryError m = (Right <$> m) `catchError` \es e -> pure (Left (es, e))
65+tryError m = (Right <$> m) `catchError` \es e -> pure $ Left (es, e)
60----------------------------------------
67----------------------------------------
63−data WrapErr e = WrapErr [String] e
70+data WrapErr e = WrapErr Unique [String] e
65instance Typeable e => Show (WrapErr e) where
72instance Typeable e => Show (WrapErr e) where
66− show (WrapErr _ e) = "WrapErr " ++ show (typeOf e)
73+ showsPrec p (WrapErr _ cs e)
74+ = showsPrec p "Effectful.Error.WrapErr ("
75+ . showsPrec p (typeOf e)
67instance Typeable e => Exception (WrapErr e)
78instance Typeable e => Exception (WrapErr e)
80+catchErrorIO :: Typeable e => Unique -> IO a -> ([String] -> e -> IO a) -> IO a
81+catchErrorIO tag m handler = do
82+ m `catch` \err@(WrapErr etag e cs) -> do
87+tryErrorIO :: Typeable e => Unique -> IO a -> IO (Either ([String], e) a)
88+tryErrorIO tag m = catchErrorIO tag (Right <$> m) $ \es e -> pure $ Left (es, e)