I'm reading RWH, and I've come to chapter 9. It introduces the following piece of code:
import System.IO
import Control.Exception
saferFileSize :: FilePath -> IO (Maybe Integer)
saferFileSize path = handle (\_ -> return Nothing) $ do
h <- openFile path ReadMode
size <- hFileSize h
hClose h
return (Just size)
It won't compile however, giving the following error message:
test.hs:5:22:
Ambiguous type variable `e0' in the constraint:
(Exception e0) arising from a use of `handle'
Probable fix: add a type signature that fixes these type variable(s)
In the expression: handle (\ _ -> return Nothing)
In the expression:
handle (\ _ -> return Nothing)
$ do { h <- openFile path ReadMode;
size <- hFileSize h;
hClose h;
return (Just size) }
In an equation for `saferFileSize':
saferFileSize path
= handle (\ _ -> return Nothing)
$ do { h <- openFile path ReadMode;
size <- hFileSize h;
hClose h;
.... }
What is going wrong here? Why won't it compile?
Not too long after RWH came out, the exception interface was changed to support more flexible handlers where the type of the handler determines which exceptions it will catch. E.g. a handler which takes SomeException
will catch anything (not usually a good idea), while a handler that takes IOException
will only catch IO exceptions.
As a consequence of this, it's easy to run into ambiguity problems with "do-nothing" handlers like the one in your example, since the compiler can't infer what type of exceptions you're trying to catch. An easy way of fixing this is to provide a type signature for your handler function.
handle ((\_ -> return Nothing) :: IOException -> IO (Maybe Integer)) $ do ...
Though, this can be somewhat verbose. An alternative solution is to specialize handle
.
handleIO :: (IOException -> IO a) -> IO a -> IO a
handleIO = handle
Then, you can just use handleIO
whenever you want to handle IO exceptions, without having to spell out the type signature of the handler.
saferFileSize path = handleIO (\_ -> return Nothing) $ do ...
A third option is to use the ScopedTypeVariables
extension, which (among other things) allows you to provide a type annotation for just the argument of a function, allowing the rest to be inferred.
{-# LANGUAGE ScopedTypeVariables #-}
saferFileSize path = handle (\(_ :: IOException) -> return Nothing) $ do ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With