The file below models a problem I have been trying to figure out.
This file simplifies my original code, while still illustrating
the problem.

> import Prelude hiding (catch)
> import Control.Monad.Reader
> import Control.Monad.Error
> import Control.Exception
> import System.IO(readFile)
> import Data.Either(either)


Our monad transformer is an ErrorT which wraps the IO monad.
ErrorT allows us to use throwError, but we won't use it in this example.

> type T a = ErrorT String IO a

The following runs a (T a) in the context of the IO monad.
We wrap runErrorT in try so as to catch things like division
by zero and what not.

> doTinIO :: T a -> IO (Either String a)
> doTinIO ta = do
>  exesa <- try (runErrorT ta) -- IO (Either Exception (Either String a))
>  return $ case exesa  of
>    Left  x   -> Left ("EX: "++(show x))
>    Right esa -> esa
>
> boom = 1 `div` 0
> b1 = return boom :: T Int
> bad = doTinIO b1

The above, bad, results in:
   Right *** Exception: divide by zero
My hope was to get
   Left "EX: divide by zero"

I cannot understand why the `try' does not get a chance at the
erroneous calculation. That is, I want the try to catch
the runtime exception.

Indeed, if the IO computation is strictly computed, I  get
the proper result.

> g1 = boom `seq` (return boom :: T Int)
> good = doTinIO g1

Stuff that raises exceptions in IO actions does not work either.

> g2 = doTinIO (return boom)

Results in: ``Right *** Exception: divide by zero''
However, other actions that do raise errors work correctly.

It appears the value of the computation must be used
as the next two examples show.

> g3 = doTinIO (liftIO (readFile "nonexistent"))
> g4 = doTinIO (liftIO (print boom))

My problem is that I control `doTinIO', but someone else provides
the computation (T a). I cannot force callers to strictly evaluate
their computations.

I've tried three other variants (given below) that are all
nearly equivalent.

> handler :: Exception -> IO (Either String a)
> handler = return . Left . ("EX: "++) . show
>
> doTinIO2 :: T a -> IO (Either String a)
> doTinIO2 ta = catch (runErrorT ta >>= evaluate) handler
>
> doTinIO3 :: T a -> IO (Either String a)
> doTinIO3 ta = do
>  esa <- catch (runErrorT ta) handler
>  case esa of
>    Right a -> catch (evaluate (return a)) handler
>    l -> return l
>
> doTinIO4 :: T a -> IO (Either String a)
> doTinIO4 ta = catch (runErrorT ta) handler

*Main> doTinIO2 b1
Right *** Exception: divide by zero
*Main> doTinIO3 b1
Right *** Exception: divide by zero
*Main> doTinIO4 b1
Right *** Exception: divide by zero

Any suggestions? Thanks all.
_______________________________________________
Haskell-Cafe mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to