Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which monad to use in Haskell for aggregating exceptions that may happen while executing a sequence of statements?

I am looking for the most common way to do something like:

x :: IO ((),[SomeException])
x = do
  void y
  void z

aggregating exceptions that may be thrown by y and z and returning them as part of the type of x.

Is there a well-known monad / tutorial for this?

like image 658
Cetin Sert Avatar asked Dec 16 '22 03:12

Cetin Sert


2 Answers

So the important question here is augustss - "If y throws an exception, what would the value of a be?"

If you have default values for a, and b, you can use try to catch your exceptions and aggregate them using WriterT:

x :: IO (C, [SomeException])
x = runWriterT $ do 
  a <- rescue defaultA y 
  b <- rescue defaultB z 
  return $ f a b

rescue :: a -> IO a -> WriterT [SomeException] IO a
rescue a m = do
  res <- lift $ try m
  case res of
    Left e -> do
      tell [e]
      return a
    Right a' -> return a'

data A
data B
data C

y :: IO A
y = undefined

defaultA :: A
defaultA = undefined

z :: IO B
z = undefined

defaultB :: B
defaultB = undefined

f :: A -> B -> C
f = undefined

Without defaults though, you can't rescue exceptions and continue computation.

like image 109
rampion Avatar answered May 19 '23 05:05

rampion


If y throws an exception, then you will never reach z. Likewise, if z throws an exception, that means that y has not thrown an exception. So the typical way to track exceptions is just tracking one -- the one that was thrown -- with ErrorT.

x :: ErrorT SomeException IO Foo
x = do
  a <- y
  b <- z
  return $ f a b

useX :: IO Quux
useX = do
  errOrVal <- runErrorT x
  case errOrVal of
    Left err -> logError err >> quux1
    Right v  -> quux2 v

Here are the type assumptions I used:

{-# LANGUAGE EmptyDataDecls #-}

import Control.Monad.Error

data Foo; data Bar; data Baz; data Quux; data SomeException

instance Error SomeException

y :: ErrorT SomeException IO Bar;   y = undefined
z :: ErrorT SomeException IO Baz;   z = undefined
f :: Bar -> Baz -> Foo;             f = undefined
quux1 :: IO Quux;                   quux1 = undefined
quux2 :: Foo -> IO Quux;            quux2 = undefined
logError :: SomeException -> IO (); logError = undefined
like image 43
Dan Burton Avatar answered May 19 '23 07:05

Dan Burton