Understanding `bracket` Function - haskell

Looking at the bracket function from Parallel and Concurrent Programming in Haskell:
bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket before after during = do
a <- before
c <- during a `onException` after a
after a
return c
In the event of an exception, why does the after function only get called once? In other words, I'm confused at the apparent execution of after a twice in the event of an exception.
But, the code shows that after a only gets called, as I understand, once:
λ: >data MyException = MyException deriving Show
λ: >instance Exception MyException
λ: >let e = return MyException :: IO MyException
λ: >bracket e (const $ putStrLn "clean up!") return
clean up!
MyException

From the docs for bracket:
When you want to acquire a resource, do some work with it, and then release the resource, it is a good idea to use bracket, because bracket will install the necessary exception handler to release the resource in the event that an exception is raised during the computation. If an exception is raised, then bracket will re-raise the exception (after performing the release).
And onException:
Like finally, but only performs the final action if there was an exception raised by the computation.
So if there is an exception thrown from during a, the first call to after a is executed, then the exception is rethrown, skipping the second after a; if there is no exception, only the second is executed.
Note that in your sample code, you’re returning an exception, not throwing it—to throw, you need to use throw or preferably throwIO :: Exception e => e -> IO a.

Related

Exception handling and purity in Haskell

In The acquire-use-release cycle section from Real World Haskell, the type of bracket is shown:
ghci> :type bracket
bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
Now, from the description of bracket, I understand that an exception might be thrown while the function of type a -> IO c is running. With reference to the book, this exception is caught by the calling function, via handle:
getFileSize path = handle (\_ -> return Nothing) $
bracket (openFile path ReadMode) hClose $ \h -> do
size <- hFileSize h
return (Just size)
I can't help but thinking that when the exception does occur from within bracket's 3rd argument, bracket is not returning an IO c.
How does this go well with purity?
I think the answer might be exactly this, but I'm not sure.
I can't help but thinking that when the exception does occur from within bracket's 3rd argument, bracket is not returning an IO c.
Prelude> fail "gotcha" :: IO Bool
*** Exception: user error (gotcha)
As you note, no Bool (respectively, c) value is produced. That's ok, because the action does not conclude – instead it re-raises the exception. That exception might then either crash the program, or it might be caught again somewhere else in calling code – importantly, whoever catches it will a) not get the result value (“the c”), you never do that in case of an exception; b) doesn't need to worry about closing the file handle, because that has already been done by bracket.

Dealing with IO exceptions inside the MonadError

I'm writing a function that queries a REST end-point to fetch some data, say queryServer:
queryServer :: (MonadIO m, MonadError Message m) => URL -> m String
The reason why I want to use the MonadError from Control.Monad.Except is that I want to have the short-circuiting behavior of this monad. So for instance it can be used as follows:
queryServers :: (MonadIO m, MonadError Message m) => m (String, String)
queryServers = do
res0 <- queryServer "server0"
res1 <- queryServer "server1"
return (res0, res1)
In this way, I do not have to worry about catching exceptions originated at queryServer and the function fails as soon as an error is encountered.
The problem I have with this approach (and there might be others I'm unaware of) is that I have to use some boilerplate to catch the IO exceptions and re-throw them with throwError:
queryServer url = do
result <- liftIO $ try (fetchSomething url)
case result of
Left (e :: IOException) -> throwError "error when fetching"
Right sth -> return sth
fetchSomething :: URL -> IO String
fetchSomething = undefined
I've seen several questions regarding the proper use of exceptions in Haskell, like this one, however none of them deals with the MonadError.
What is the right way to dealing with IO exceptions within the `MonadError?

Catch SomeException with ExceptT

I'm trying to use the ExceptT monad transformer to catch any exception thrown by a function, like so:
import Control.Exception
import Control.Monad.Trans.Except
badFunction :: ExceptT SomeException IO ()
badFunction = throw DivideByZero
main :: IO ()
main = do
r <- runExceptT badFunction
case r of Left _ -> putStrLn "caught error"
Right _ -> putStrLn "nope, didn't catch no error"
... but the exception happily flies by. What am I doing wrong?
Edit: to clarify, the aim is to catch any exception thrown by a function, regardless of how the exception was thrown. If it makes any difference, the real function call is at the bottom of a fairly deep monad transformer stack. I don't mind missing things like thrown strings (bad programmer!).
First, you catch your runtime exception. It can be done by using either monad-control (and lifted-base) or exceptions. Michael Snoyman has a nice article comparing the two: Exceptions and monad transformers
Second, you embed the caught exception in ExceptT.
Here's the complete working code:
import Control.Exception.Lifted
import Control.Monad.Trans.Except
badFunction :: ExceptT SomeException IO ()
badFunction = throw DivideByZero
intercept
:: ExceptT SomeException IO a
-> ExceptT SomeException IO a
intercept a = do
r <- try a
case r of
Right x -> return x
Left e -> throwE e
main :: IO ()
main = do
r <- runExceptT $ intercept badFunction
case r of Left _ -> putStrLn "caught error"
Right _ -> putStrLn "nope, didn't catch no error"
A more compact (but perhaps somewhat less obvious) definition of intercept is
intercept
:: ExceptT SomeException IO a
-> ExceptT SomeException IO a
intercept = handle throwE
I believe you want throwE, not throw.
Also, it'll be an ArithException, not a SomeException.

How do I stop evaluating a list of functions once I've caught an exception

I have three functions I map over, I wish to stop evaluation upon catching an exception.
I can catch the exception, but am not getting the behavior I want. It could be that I'm thinking about this problem in the incorrect way (maybe I shouldn't be map a list of functions in this case), and would appreciate this being pointed out. Here's what I think is the relevant code.
import qualified Control.Exception as C
data JobException = PreProcessFail
| JobFail
| ChartFail
deriving (Show, Typeable)
instance C.Exception JobException
type ProcessState = MVar ProcessConfig
data ProcessConfig = PConfig { model :: ServerModel
, ipAddress :: String
, cookie :: Cookie
} deriving Show
exceptionHandler :: JobException -> IO ()
exceptionHandler exception = do
writeFile "testException.txt" ("caught exception " ++ (show exception))
-- much more functionality will be put here once I get the logic correct
preProcess :: ProcessState -> IO ()
preProcess sModel = do
putStrLn ("preProcessing" )
initiateJob :: ProcessState -> IO ()
initiateJob sModel = do
C.throw JobFail
putStrLn ("in progress")
makeChart :: ProcessState -> IO ()
makeChart sModel = do
putStrLn ("chart making")
So now, when I test this out in ghci, this is what happens.
a <- mapM (flip Control.Exception.catch exceptionHandler) [preProcess world, initiateJob world, makeChart world]
Loading package filepath-1.2.0.0 ... linking ... done.
Loading package unix-2.4.2.0 ... linking ... done.
preProcessing
chart making
I should not be seeing the string "chart making". How do I abort the evaluation of the list upon throwing an exception?
mapM maps the function then sequences the list. So you've got a catch around each action in the list separately. What you want is to sequence the list into a single action, and then catch the exception once so interrupting everything else in the list. The following works:
(flip Control.Exception.catch exceptionHandler) $ sequence_ [preProcess world, initiateJob world, makeChart world]

Ambiguous type variable error msg

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>
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
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.
A workaround is to use Control.OldException in ghc 6.10.* instead of Control.Exception.
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
"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

Resources