Catching string from error function with GHC Control.Exception

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Catching string from error function with GHC Control.Exception

Andrew Savige
I am a Haskell beginner, using GHC 6.4.1.

Suppose I have a function, tt, say, which may call error.
For the purposes of testing tt, I'd like to be able to catch
the error string and assert that it matches the expected error
string. Is HUnit or QuickCheck (or either) the appropriate tool
for this sort of testing?

I managed to get it working with HUnit, but it's horribly kludgy
mainly because I'm yet to grok monads. The little test program
below demonstrates my question outside of HUnit:

import Control.Exception

tt :: String -> Int
tt s = error ("hi error '" ++ s ++ "'")
-- tt s = read s

ttWrap :: String -> IO String
ttWrap x = do let r = tt x
              -- comment out next line, error string not caught
              putStr (show r)
              return (show r)

ttWrap2 :: IO String
ttWrap2 = do res <- tryJust errorCalls (ttWrap "123")
             case res of
               Right r  -> return (show r)
               Left  r  -> return r

main = do x <- ttWrap2
          putStrLn ("caught:" ++ x)
          y <- ttWrap2
          putStrLn ("caught:" ++ y)

When you run the above test program, it does indeed catch the
error string thrown by the tt function (and prints it out in
main). However, if you comment out the putStr line as shown in
the comment above, it no longer catches the error string, but
dies instead. Why?

I'm sure there is a cleaner way to achieve what I'm trying to
do and I'd appreciate advice on the best way to do this.

Thanks,
/-\


Send instant messages to your online friends http://au.messenger.yahoo.com 
_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Catching string from error function with GHC Control.Exception

Cale Gibbard
On 01/01/06, Andrew Savige <[hidden email]> wrote:

> I am a Haskell beginner, using GHC 6.4.1.
>
> Suppose I have a function, tt, say, which may call error.
> For the purposes of testing tt, I'd like to be able to catch
> the error string and assert that it matches the expected error
> string. Is HUnit or QuickCheck (or either) the appropriate tool
> for this sort of testing?
>
> I managed to get it working with HUnit, but it's horribly kludgy
> mainly because I'm yet to grok monads. The little test program
> below demonstrates my question outside of HUnit:
>
> import Control.Exception
>
> tt :: String -> Int
> tt s = error ("hi error '" ++ s ++ "'")
> -- tt s = read s
>
> ttWrap :: String -> IO String
> ttWrap x = do let r = tt x
>               -- comment out next line, error string not caught
>               putStr (show r)
>               return (show r)
>
> ttWrap2 :: IO String
> ttWrap2 = do res <- tryJust errorCalls (ttWrap "123")
>              case res of
>                Right r  -> return (show r)
>                Left  r  -> return r
>
> main = do x <- ttWrap2
>           putStrLn ("caught:" ++ x)
>           y <- ttWrap2
>           putStrLn ("caught:" ++ y)
>
> When you run the above test program, it does indeed catch the
> error string thrown by the tt function (and prints it out in
> main). However, if you comment out the putStr line as shown in
> the comment above, it no longer catches the error string, but
> dies instead. Why?
>

The error never happens until r (which is tt x) is actually evaluated.
Applying putStr to (show r) will certainly demand the evaluation
because it will be needed for IO, but nothing about return (show r)
forces this evaluation to occur -- an unevaluated thunk will be
returned. So the error doesn't happen and so doesn't get caught. The
Right branch of the case is taken, which still doesn't cause a
failure. The failure occurs when the value gets back to main and you
try to print it.

So you need to ensure the evaluation of r occurs in a particular order
relative to IO. Control.Exception.evaluate does this -- it forces its
parameter to be evaluated up to the top level constructor before IO
proceeds, which should cause the error to occur and be caught. Note
that if tt returned some more complicated structure, with the error
hidden deep in the structure, you might need to use seq recursively,
or just one of the DeepSeq-type libraries out there. (GHC includes
Control.Parallel.Strategies which takes care of this, though it's not
well documented in the libraries. myStructure `using` rnf will do the
same as deepSeq does.)

Hope this helps,
 - Cale
_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Catching string from error function with GHC Control.Exception

Iain Alexander
In reply to this post by Andrew Savige
I've just been through the process of converting some code from using Control.Exception to
using an Error monad, and I would recommend that as a more straightforward and
manageable alternative, unless you need Control.Exception for other reasons.

I started by changing my potentially-failing functions to return Either Exception a,
where Exception is my own user-defined error type, but you could use String, and a is the
type of the "real" return value.  I initially used explicit Left and Right to construct appropriate
values.  My testing code is hand-written, and explicitly matched against Left e and Right x to
decode the return value.

I ended up with functions returning MonadError Exception m => m a, using throwError and
return to construct appropriate values.  My testing code uses
ErrorT Exception (StateT s IO) a
to manage a combination of error-handling, state (an error count) and IO, with the option of
using Either Exception to test expected errors for individual cases.
--
Iain Alexander      [hidden email]


_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe