Exception handling and purity in Haskell - 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.

Related

How to safely `mapM` over `System.IO.openFile`

My application needs to have several resources open during runtime. I am achieving this by mapping over openFile once at the start of the application.
let filePaths = ["/dev/ttyUSB0", "/dev/ttyUSB1", "/dev/ttyUSB2"]
fileHandles <- mapM (`openFile` ReadWriteMode) filePaths
This code is not safe as it might work for the first 2 file paths but then throw an exception when opening the 3rd file path. In that case i need to close the first 2 file paths that were opened already so i can exit the function without leaking resources. i have looked at the functions and patterns from Control.Exception but not found anything that helps in this case. I have not looked at ResourceT yet. Does it help in this situation?
I think i am looking for a function signature that is similar to this:
safeMapM:: [a] -> (a -> IO b) -> (b -> IO()) -> [b]
where (b -> IO()) is the clean up function that is called when some exception occurred.
Solutions i could think of that are probably not good:
wrapping each element in a Maybe. Exceptions can be catched and result in a Nothing. the mapM could always finish and i could check for Nothing/Exception afterwards and then close all successfully opened file handles by their Just Handle.
using fold instead of map. when an exception at the current element occurs i can close the file handles of all previous elements of the fold and then rethrow the exception to stop the fold from continuing.
If I understand correctly, the problem is how to ensure safe closing of all handles when an exception happens.
For a single file, the usual way of ensuring safety is withFile. The complication here is that you want to open a sequence of files.
Perhaps we could write this auxiliary function that performs nested allocations of withFile and passes the list of Handles to a callback in the innermost level:
nestedWithFile :: [FilePath] -> IOMode -> ([Handle] -> IO r) -> IO r
nestedWithFile filePaths mode callback = go [] filePaths
where
go acc [] =
callback acc -- innermost invocation, protected by the withFiles
go acc (p : ps) =
withFile p mode (\handle -> go (acc ++ [handle]) ps)
Another way of doing it begins by realizing that we are doing something with a replicateM flavor: we are performing an "effect" n times, and returning a list with the results. But what would be the "effect" (that is, the Applicative) here? It seems to be "protecting the allocation of a resource with a wrapper function that ensures release".
This kind of effect seems to require some control over the "rest of the computation", because when the "rest of the computation" finishes in any way, the finalizer must still be run. This points us to the continuation monad transformer, ContT:
import Control.Monad
import Control.Monad.Trans.Cont
import System.IO
openFile' :: FilePath -> ContT r IO Handle
openFile' filePath = ContT (withFile filePath ReadWriteMode)
openSameFileSeveralTimes :: Int -> FilePath -> ContT r IO [Handle]
openSameFileSeveralTimes count filePath = replicateM count (openFile' filePath)
-- The handles are freed when the ([Handle] -> IO r) callback exits
useHandles :: ContT r IO [Handle] -> ([Handle] -> IO r) -> IO r
useHandles = runContT
The continuation transformer might be a bit too general for this purpose. There are libraries like managed which follow the same basic mechanism but are more focused on resource handling.

Is h a handle or a lambda function (or both)?

I am looking at a simple IO program from the Haskell Wikibook. The construction presented on that page works just fine, but I'm trying to understand "how".
The writeChar function below takes a filepath (as a string) and a character, and it writes the character to the file at the given path. The function uses a bracket to ensure that the file opens and closes properly. Of the three computations run in the bracket, the "computation to run in-between"---as I understand it---is a lambda function that returns the result of hPutChar h c.
Now, hPutChar itself has a declaration of hPutChar :: Handle -> Char -> IO (). This is where I'm lost. I seem to be passing h as the handle to hPutChar. I would expect a handle somehow to reference the file opened as fp, but instead it appears to be recursively calling the lambda function \h. I don't see how this lambda function calling itself recursively knows to write c to the file at fp.
I would like to understand why the last line of this function shouldn't read (\h -> hPutChar fp c). Attempting to run it that way results in "Couldn't match type ‘[Char]’ with ‘Handle’" which I consider sensible given that hPutChar expects a Handle datatype as opposed to a string.
import Control.Exception
writeChar :: FilePath -> Char -> IO ()
writeChar fp c =
bracket
(openFile fp WriteMode)
hClose
(\h -> hPutChar h c)
Let's have a look at the type of bracket (quoted as it appears in your Haskell Wiki link):
bracket :: IO a -- computation to run first ("acquire resource")
-> (a -> IO b) -- computation to run last ("release resource")
-> (a -> IO c) -- computation to run in-between
-> IO c
In your use case, the first argument, openFile fp WriteMode is an IO Handle value, a computation that produces a handle corresponding to the fp path. The third argument, \h -> hPutChar h c, is a function that takes a handle and returns a computation that writes to it. The idea is that the function you pass as the third argument specifies how the resource produced by the first argument will be used.
There is no recursion going on here. h is indeed a Handle. If you've programmed in C, the rough equivalent is a FILE. The handle consists of a file descriptor, buffers, and whatever else is needed to perform I/O on the attached file/pipe/terminal/whatever. openFile takes a path, opens the requested file (or device), and provides a handle that you can use to manipulate the requested file.
bracket
(openFile fp WriteMode)
hClose
(\h -> hPutChar h c)
This opens the file to produce a handle. That handle is passed to the third function, which binds it to h and passes it to hPutChar to output a character. Then in the end, bracket passes the handle to hClose to close the file.
If exceptions didn't exist, you could implement bracket like this:
bracket
:: IO resource
-> (resource -> IO x)
-> (resource -> IO a)
-> IO a
bracket first last middle = do
resource <- first
result <- middle resource
last resource
pure result
But bracket actually has to install an exception handler to endure that last is called even if an exception occurs.
hPutChar :: Handle -> Char -> IO ()
is a pure Haskell function, which, given two arguments h :: Handle and c :: Char, produces a pure Haskell value of type IO (), an "IO action":
h :: Handle c :: Char
---------------------------------------------
hPutChr h c :: IO ()
This "action" is simply a Haskell value, but when it appears inside the IO monad do block under main, it becomes executed by the Haskell run-time system and then it actually performs the I/O operation of putting a character c into a filesystem's entity referred to by the handle h.
As for the lambda function, the actual unambiguous syntax is
(\ h -> ... )
where the white space between \ and h is optional, and the whole (.......) expression is the lambda expression. So there is no "\h entity":
(\ ... -> ... ) is the lambda-expression syntax;
h in \ h -> is the lambda function's parameter,
... in (\ h -> ... ) is the lambda function's body.
bracket calls the (\h -> hPutChar h c) lambda function with the result produced by (openFile fp WriteMode) I/O computation, which is the handle of the file name referred to by fp, opened according to the mode WriteMode.
Main thing to understand about the Haskell monadic IO is that "computation" is not a function: it is the actual (here, I/O) computation performing the actual file open -- which produces the handle -- which is then used by the run-time system to call the pure Haskell function (\ h -> ...) with it.
This layering (of pure Haskell "world" and the impure I/O "world") is the essence of .... yes, Monad. An I/O computation does something, finds some value, uses it to call a pure Haskell function, which creates a new computation, which is then run, feeds its results into the next pure function, etc. etc.
So we keep our purity in Haskell by only talking about the impure stuff. Talking is not doing.
Or is it?

Understanding `bracket` Function

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.

How to completely abort a Haskell runtime?

I have a function
import System.Exit
exit_and_report_type_mismatch :: String -> IO ExitCode
exit_and_report_type_mismatch error_message = do
putStrLn error_message
exitFailure
and a section of another like so
interpret_expr :: Vars -> Expr -> Val
interpret_expr vars (Plus (ConsE _ _) (NumE _)) = exit_and_report_type_mismatch "Type Error: Can only concatenate list (not int) to list"
Haskell complains to me that it is expecting type Val (another data type I have defined) but it actually receives type IO Exitcode. Fair enough - exit_and_report_mismatch is returning IO ExitCode which is not a Val.
How do I completely abort the Haskell program from within "exit_and_report_type_mismatch"? I have read a bit about Haskell exceptions but the explanations either do not make sense or mention having to call ExitWith from the main function, which is not an option.
This is what error is for. From the documentation:
error :: [Char] -> a
error stops execution and displays an error message.
For instance:
zsh% runhaskell <<<'main = putStrLn (error "Message") >> print "Not reached."'
runghcXXXX7729.hs: Message
The effect of putStrLn is ignored, and the program terminates as soon as the value produced by error is demanded (lazy evaluation means that just putting error somewhere doesn't immediately cause an error; as you might or might not expect, let x = error "Message" in putStrLn "Printed" causes no errors). It is possible to catch these exceptions with the functions from Control.Exception.Base, such as catch, but I've never done this nor have I seen this done.
Also, as a final note, consider avoiding the use of error. Partial functions (functions that aren't defined over their entire input domain) are best avoided when possible, as it's much easier to reason about your code with the stronger guarantees total functions provide. It's nice when, as for total functions, f :: A -> B really means "the function f returns something of type B"; for partial functions, f :: A -> B means only "if the function f returns, then what it returns is of type B". In your case, this might mean having a type like interpretExpr :: Vars -> Expr -> Either RuntimeError Val, or something suitably isomorphic (in the simplest case, perhaps data Result = Error String | Value Val, and interpretExpr :: Vars -> Expr -> Result).
This will do it:
import System.IO.Unsafe
exit_and_report_type_mismatch :: String -> a
exit_and_report_type_mismatch error_message = unsafePerformIO $ do
putStrLn error_message
exitFailure
The function error might work the same though.

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