Haskell Either Error Handling
Contents |
the Maybe MonadUse of EitherCustom Data Types for ErrorsMonadic Use of EitherExceptionsFirst Steps with ExceptionsLaziness and Exception HandlingUsing handleSelective Handling of ExceptionsI/O ExceptionsThrowing ExceptionsDynamic ExceptionsExercisesError handling in monadsA tiny parsing frameworkExercises Error handling haskell either example is one of the most important—and overlooked—topics for programmers, regardless of haskell throw exception the language used. In Haskell, you will find two major types of error handling employed: "pure" error handling
Haskell Either Monad
and exceptions. When we speak of "pure" error handling, we are referring to algorithms that do not require anything from the IO monad. We can often implement error
Haskell Error Monad
handling for them by simply using Haskell's expressive data type system to our advantage. Haskell also has an exception system. Due to the complexities of lazy evaluation, exceptions in Haskell can be thrown anywhere, but only caught within the IO monad. In this chapter, we'll consider both. Error Handling with Data Types Let's begin our discussion of error handling definition error handling with a very simple function. Let's say that we wish to perform division on a series of numbers. We have a constant numerator, but wish to vary the denominator. We might come up with a function like this: -- file: ch19/divby1.hs divBy :: Integral a => a -> [a] -> [a] divBy numerator = map (numerator `div`) Very simple, right? We can play around with this a bit in ghci: ghci> divBy 50 [1,2,5,8,10] [50,25,10,6,5] ghci> take 5 (divBy 100 [1..]) [100,50,33,25,20] This behaves as expected: 50 / 1 is 50, 50 / 2 is 25, and so forth. [38] This even worked with the infinite list [1..]. What happens if we sneak a 0 into our list somewhere? ghci> divBy 50 [1,2,0,8,10] [50,25,*** Exception: divide by zero Isn't that interesting? ghci started displaying the output, then stopped with an exception when it got to the zero. That's lazy evaluation at work—it calculated results as needed. As we will see later in this chapter, in the absence
in Haskell-Cafe and more and more packages that handle errors and exceptions or something between. Although both terms are
Error Handling Java
related and sometimes hard to distinguish, it is important to do types of error handling it carefully. This is like the confusion between parallelism and concurrency. The first problem is that "exception" data error handling seems to me to be the historically younger term. Before there were only "errors", independent of whether they were programming, I/O or user errors. In this article http://book.realworldhaskell.org/read/error-handling.html we use the term exception for expected but irregular situations at runtime and the term error for mistakes in the running program that can be resolved only by fixing the program. We do not want to distinguish between different ways of representing exceptions: Maybe, Either, exceptions in IO monad, or return codes, they all represent exceptions https://wiki.haskell.org/Error_vs._Exception and are worth considering for exception handling. The history may have led to the identifiers we find today in the Haskell language and standard Haskell modules. Exceptions: Prelude.catch, Control.Exception.catch, Control.Exception.try, IOError, Control.Monad.Error Errors: error, assert, Control.Exception.catch, Debug.Trace.trace Note, that the catch function from Prelude handles exclusively exceptions, whereas its counterpart from Control.Exception also catches certain kinds of undefined values. Prelude> catch (error "bla") (\msg -> putStrLn $ "caught " ++ show msg) *** Exception: bla Prelude> Control.Exception.catch (error "bla") (\msg -> putStrLn $ "caught " ++ show (msg::Control.Exception.SomeException)) caught bla This is unsafe, since Haskell's error is just sugar for undefined, that shall help spotting a programming error. A program should work as well when all errors and undefineds are replaced by infinite loops. However infinite loops in general cannot be caught, whereas calls to sugared functions like error can. Even more confusion was initiated by the Java programming language to use the term "exceptions" for programming errors like the NullPointerException and introducing the distincti
3.1.1 control-monad-exception 3.1.2 attempt 4 safe-failure 5 Stack traces 6 See also 7 References 1 Exceptions, errors, failures oh my! Terminology is a little confusing. There's a few different things floating around: Error usually refers to a https://wiki.haskell.org/Failure programming error. It can also refer to the "error" function, which in fact causes a runtime exception to be triggered. Exception usually refers to an exception which is thrown in the IO monad. It can also refer to the actual typeclass "Exception" which was introduced along with extensible exceptions. Fail is referring to the "fail" function, which is part of the Monad typeclass and is almost universally despised. To avoid the baggage and name error handling clashes introduced with all of the above, we use the term failure. This name is used consistently for module names, type classes, function names and the abstract concept. But what is a failure? It's any time that something does not succeed. Of course, this depends on your definition of success. Here are some examples: readInt fails when it receives invalid input. head fails when it receives an empty list. lookup fails when the key haskell either error is not found in the list. at (also known as!!) fails when the index is past the end of the list. Now that we know what a failure is, let's discuss how to deal with it. 2 Prior Art There are currently a number of methods available for dealing with failures. However, all of them are lacking in some way: Maybe. However, there's no information on *what* failed. The error function. But not all failures should be fatal. IO exceptions. But they force your code into your IO monad, plus it's non-obvious that a function might fail with an exception based solely on its return type. Custom error values. But how do you compose two libraries together? The Either/ErrorT monads. The main problem with these lies in composability and extensibility. This abundance of error handling methods leads to lots of gluing code when combining libraries which use different notions of failure. Examples: The partial functions in the Prelude and other base modules, such as head, tail, fromJust, lookup, etc. use the error function. parsec models failure using Either ParseError http models failure using Either ConnError The HDBC package models failure using the SQLError exception The cgi package uses exceptions Quoting from Eric Kidd: Consider a program to download a web page and parse it: 1. Network.URI.parseURI returns (Maybe URI). 2. Network.HTTP.simpleH