22
votes

I don't think it is a bug, but I am a bit puzzled as to why that doesn't work. A bonus question is why does it mention variable e? There is no variable e.

    Prelude> :m +Control.Exception
    Prelude Control.Exception> handle (\_-> return "err") undefined

    <interactive>:1:0:
        Ambiguous type variable `e' in the constraint:
          `Exception e'
            arising from a use of `handle' at <interactive>:1:0-35
        Probable fix: add a type signature that fixes these type variable(s)
    Prelude Control.Exception> 

Apparently it works fine in ghci 6.8, I am using 6.10.1.

Edit: I have minimized the code. I expect that to have the same result in both 6.8 and 6.10

class C a                                                                                                     

foo :: C a => (a -> Int)-> Int                                                                                
foo _ = 1                                                                                                     

arg :: C a => a -> Int                                                                                        
arg _ = 2                                                                                                     

bar :: Int                                                                                                    
bar = foo arg

trying to compile it:

[1 of 1] Compiling Main             ( /tmp/foo.hs, interpreted )

/tmp/foo.hs:12:10:
    Ambiguous type variable `a' in the constraint:
      `C a' arising from a use of `arg' at /tmp/foo.hs:12:10-12
    Probable fix: add a type signature that fixes these type variable(s)
Failed, modules loaded: none.
Prelude Control.Exception> 
5

5 Answers

12
votes

The type of Control.Exception.handle is:

handle :: Exception e => (e -> IO a) -> IO a -> IO a

The problem you are seeing is that the lambda expression (\_ -> return "err") is not of type e -> IO a where e is an instance of Exception. Clear as mud? Good. Now I'll provide a solution which should actually be useful :)

It just so happens in your case that e should be Control.Exception.ErrorCall since undefined uses error which throws ErrorCall (an instance of Exception).

To handle uses of undefined you can define something like handleError:

handleError :: (ErrorCall -> IO a) -> IO a -> IO a
handleError = handle

It's essentially an alias Control.Exception.handle with e fixed as ErrorCall which is what error throws.

It looks like this when run in GHCi 7.4.1:

ghci> handleError (\_ -> return "err") undefined
"err"

To handle all exceptions a handleAll function can be written as follows:

handleAll :: (SomeException -> IO a) -> IO a -> IO a
handleAll = handle

Catching all exceptions has consequences described well in this excerpt of the Control.Exception documentation:

Catching all exceptions

It is possible to catch all exceptions, by using the type SomeException:

catch f (\e -> ... (e :: SomeException) ...)

HOWEVER, this is normally not what you want to do!

For example, suppose you want to read a file, but if it doesn't exist then continue as if it contained "". You might be tempted to just catch all exceptions and return "" in the handler. However, this has all sorts of undesirable consequences. For example, if the user presses control-C at just the right moment then the UserInterrupt exception will be caught, and the program will continue running under the belief that the file contains "". Similarly, if another thread tries to kill the thread reading the file then the ThreadKilled exception will be ignored.

Instead, you should only catch exactly the exceptions that you really want. In this case, this would likely be more specific than even "any IO exception"; a permissions error would likely also want to be handled differently. Instead, you would probably want something like:

 e <- tryJust (guard . isDoesNotExistError) (readFile f)
 let str = either (const "") id e

There are occassions when you really do need to catch any sort of exception. However, in most cases this is just so you can do some cleaning up; you aren't actually interested in the exception itself. For example, if you open a file then you want to close it again, whether processing the file executes normally or throws an exception. However, in these cases you can use functions like bracket, finally and onException, which never actually pass you the exception, but just call the cleanup functions at the appropriate points.

But sometimes you really do need to catch any exception, and actually see what the exception is. One example is at the very top-level of a program, you may wish to catch any exception, print it to a logfile or the screen, and then exit gracefully. For these cases, you can use catch (or one of the other exception-catching functions) with the SomeException type.

Source: http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception.html#g:4

10
votes

This problem shows up only in GHC 6.10; it can't be duplicated in GHC 6.8 because the type of handle is different:

: nr@homedog 620 ; ghci
GHCi, version 6.8.2: http://www.haskell.org/ghc/  :? for help
Loading package base ... linking ... done.
Prelude> :m +Control.Exception
Prelude Control.Exception>  handle (\_ -> return "err") undefined
"err"
Prelude Control.Exception> 

OK maybe I can get this right at last. I think the problem is not the monomorphism restriction, but rather you've hit an instance of the Read/Show problem: you're offering to handle some type of exception, in the new version of `handle, there is more than one type of exception, and the type of that exception does not appear in your result. So the compiler has no way of knowing which type of exception you're trying to handle. One way to work this is to pick one. Here's some code that works:

Prelude Control.Exception> let alwaysError :: SomeException -> IO String; alwaysError = \_ -> return "err"
Prelude Control.Exception> handle alwaysError undefined
"err"

Incidentally, the example use of handle in the GHC library documentation does not compile under 6.10. I have filed a bug report.

3
votes

A workaround is to use Control.OldException in ghc 6.10.* instead of Control.Exception.

2
votes

Try giving your handler the type SomeException -> IO x, where x is a concrete type, e.g.

import Control.Exception
let f _ = putStrLn "error" :: SomeException -> IO () 
in handle f undefined 
1
votes

"Exception e" is likely from the type signature of "handle".

The documentation says:

handle :: Exception e => (e -> IO a) -> IO a -> IO a

In GHC 6.8 it used to be different, which would explain why I don't get that error.

handle :: (Exception -> IO a) -> IO a -> IO a

Seems you're running into the monomorphism restriction. That "_"-Pattern must be monomorphic (which it is with ghc 6.8) or explicitly typed. A "workaround" is to put the pattern on the left hand side of a definition, where it constitutes a "simple pattern binding" as specified by the Haskell Report.

Try this:

let f _ = return "err"
handle f undefined

http://www.haskell.org/haskellwiki/Monomorphism_restriction