[More exceptions work John Goerzen **20080507054546] { hunk ./en/book-shortcuts.xml 80 +catchDyn"> hunk ./en/book-shortcuts.xml 161 +throw"> +throwIO"> hunk ./en/ch20-errors.xml 581 + + You'll notice that most of these function appear to return a + value of type a or IO + a. This means that the function can appear to + return a value of any type. In fact, because these functions + raise exceptions, they never "return" anything in the normal + sense. These return values let you use these functions in + various contexts where various different types are expected. + + + Let's start our tour of ways to raise exceptions with the + functions in Control.Exception. The + most generic function is &throw;, which has type + Exception -> a. This function can throw + any &Exception;, and can do so in a pure context. There is a + companion function &throwIO; with type Exception + -> IO a that raises an exception in tne &IO; + monad. Both of these functions require you to craft an + &Exception; by hand -- or to have already have one on hand to + raise, perhaps from a handler called by &handle;. + + + There is also a function &ioError;, which is defined + identically in both Control.Exception and + System.IO.Error with type IOError + -> IO a. This is used when you want to generate + an arbitrary I/O-related exception. + + + Perhaps most useful are two shortcut functions: &error; and + &fail;. When used in the &IO; monad, &fail; could be thought + of like this: + + +fail :: String -> IO a +fail = ioError . userError + + + So, &fail; is useful for generating an exception with an + arbitrary error message in the &IO; monad. For pure code, you + can use &error;, which is defined like this: + + +error :: String -> a +error = throw . ErrorCall + + FIXME: do we need examples here? + + + + Dynamic Exceptions + + While &fail; and &error; are often quite useful, you may not + want to use them often in reusable code. Since they take only + arbitrary strings as arguments, it can be difficult to + represent complex error data. Sometimes the built-in + exception mechanism isn't enough for you, either. In these + cases, you can use Haskell's dynamic exception support. + + + This makes use of two little-used Haskell modules: + Data.Dynamic and + Data.Typeable. We will not go into a great + level of detail on those modules here, but will give you the + tools you need to craft and use your own dynamic exception + type. + + + In , you will see that the HDBC + database library uses dynamic exceptions to indicate errors + from SQL databases back to applications. Errors from database + engines often have three components: an integer that + represents an error code, a state, and a human-readable error + message. We will build up our own implementation of the HDBC + SqlError type here in this chapter. Let's + start with the data structure representing the error itself: + + &dynexc.hs:sqlerror; + + Next, we define a bit of infrastructure to make the error type + participate in Haskell's dynamic typing system. Note that the + argument to mkTyCon must be unique + throughout your entire application. + + &dynexc.hs:dynamic; + + Now, let's define a catchSql and a + handleSql that can be used to catch an + exception that is an SqlError. Note that + the regular &catch; and &handle; functions cannot catch our + SqlError, because it is not a type of + &Exception;. + + &dynexc.hs:catch; + + These functions are simply thin wrappers around &catchDyn;, + which has type Typeable exception => IO a -> + (exception -> IO a) -> IO a. We here simply + restrict the type of this so that it catches only SQL + exceptions. + + + Normally, when an exception is raised, but not caught + anywhere, the program will crash and will display the + exception to standard error. With a dynamic exception, + however, the system will not know how to display this, so you + will simply see an unhelpful "unknown exception" message. We + can provide a utility so that application writers can simply + say main = handleSqlError $ do ..., and + have confidence that any exceptions raised (in that thread) + will be displayed. Here's how to write + handleSqlError: + + &dynexc.hs:handleSqlError; + + Finally, let's give you an example of how to raise an + SqlError as an exception. Here's a + function that will do just that: + + &dynexc.hs:throwSql; + + This completes our dynamic exception support. That was a lot + of code, and you may not have needed that much, but we wanted + to give you an example of the dynamic exception itself and the + utilities that often go with it. In fact, these examples + reflect almost exactly what is present in the HDBC library. + Let's play with these in &ghci; for a bit: + + &dynexc.ghci:e1; + + From this, you can see that &ghci; doesn't know how to display + an SQL error by itself. Howerver, you can also see that our + handleSqlError function helped out with + that, but also passed through other errors unmodified. Let's + finally try out a custom handler: + + &dynexc.ghci:e2; + + Here we defined a custom error handler that raised a new + exception consisting of the message in the + seErrorMsg field of the + SqlError. You can see that it worked as + intended. + hunk ./en/ch20-errors.xml 728 - }