| 1 | |
|---|
| 2 | New patches: |
|---|
| 3 | |
|---|
| 4 | [Converted the module documentation to Haddock format. Per Jeff Newbern's gracious permission included relevant information from his cool tutorial "All About Monads" http://www.nomaware.com/monads/. Added examples for custom error type, ErrorT. Use String instead of [Char]. |
|---|
| 5 | Andriy Palamarchuk <apa3a@yahoo.com>**20070115222741] { |
|---|
| 6 | hunk ./Control/Monad/Error.hs 4 |
|---|
| 7 | ------------------------------------------------------------------------------ |
|---|
| 8 | --- | |
|---|
| 9 | --- Module : Control.Monad.Error |
|---|
| 10 | --- Copyright : (c) Michael Weber <michael.weber@post.rwth-aachen.de>, 2001 |
|---|
| 11 | --- License : BSD-style (see the file libraries/base/LICENSE) |
|---|
| 12 | --- |
|---|
| 13 | --- Maintainer : libraries@haskell.org |
|---|
| 14 | --- Stability : experimental |
|---|
| 15 | --- Portability : non-portable (multi-parameter type classes) |
|---|
| 16 | --- |
|---|
| 17 | --- The Error monad. |
|---|
| 18 | --- |
|---|
| 19 | --- Rendered by Michael Weber <mailto:michael.weber@post.rwth-aachen.de>, |
|---|
| 20 | --- inspired by the Haskell Monad Template Library from |
|---|
| 21 | --- Andy Gill (<http://www.cse.ogi.edu/~andy/>) |
|---|
| 22 | --- |
|---|
| 23 | ------------------------------------------------------------------------------ |
|---|
| 24 | +{- | |
|---|
| 25 | +Module : Control.Monad.Error |
|---|
| 26 | +Copyright : (c) Michael Weber <michael.weber@post.rwth-aachen.de> 2001, |
|---|
| 27 | + (c) Jeff Newbern 2003-2006, |
|---|
| 28 | + (c) Andriy Palamarchuk 2006 |
|---|
| 29 | +License : BSD-style (see the file libraries/base/LICENSE) |
|---|
| 30 | + |
|---|
| 31 | +Maintainer : libraries@haskell.org |
|---|
| 32 | +Stability : experimental |
|---|
| 33 | +Portability : non-portable (multi-parameter type classes) |
|---|
| 34 | hunk ./Control/Monad/Error.hs 15 |
|---|
| 35 | +[Computation type:] Computations which may fail or throw exceptions. |
|---|
| 36 | + |
|---|
| 37 | +[Binding strategy:] Failure records information about the cause\/location |
|---|
| 38 | +of the failure. Failure values bypass the bound function, |
|---|
| 39 | +other values are used as inputs to the bound function. |
|---|
| 40 | + |
|---|
| 41 | +[Useful for:] Building computations from sequences of functions that may fail |
|---|
| 42 | +or using exception handling to structure error handling. |
|---|
| 43 | + |
|---|
| 44 | +[Zero and plus:] Zero is represented by an empty error and the plus operation |
|---|
| 45 | +executes its second argument if the first fails. |
|---|
| 46 | + |
|---|
| 47 | +[Example type:] @'Data.Either' String a@ |
|---|
| 48 | + |
|---|
| 49 | +The Error monad (also called the Exception monad). |
|---|
| 50 | +-} |
|---|
| 51 | + |
|---|
| 52 | +{- |
|---|
| 53 | + Rendered by Michael Weber <mailto:michael.weber@post.rwth-aachen.de>, |
|---|
| 54 | + inspired by the Haskell Monad Template Library from |
|---|
| 55 | + Andy Gill (<http://www.cse.ogi.edu/~andy/>) |
|---|
| 56 | +-} |
|---|
| 57 | hunk ./Control/Monad/Error.hs 45 |
|---|
| 58 | + -- * Example 1: Custom Error Data Type |
|---|
| 59 | + -- $customErrorExample |
|---|
| 60 | + |
|---|
| 61 | + -- * Example 2: Using ErrorT Monad Transformer |
|---|
| 62 | + -- $ErrorTExample |
|---|
| 63 | hunk ./Control/Monad/Error.hs 66 |
|---|
| 64 | --- --------------------------------------------------------------------------- |
|---|
| 65 | --- class MonadError |
|---|
| 66 | --- |
|---|
| 67 | --- throws an exception inside the monad and thus interrupts |
|---|
| 68 | --- normal execution order, until an error handler is reached} |
|---|
| 69 | --- |
|---|
| 70 | --- catches an exception inside the monad (that was previously |
|---|
| 71 | --- thrown by throwError |
|---|
| 72 | - |
|---|
| 73 | +-- | An exception to be thrown. |
|---|
| 74 | +-- An instance must redefine at least one of 'noMsg', 'strMsg'. |
|---|
| 75 | hunk ./Control/Monad/Error.hs 69 |
|---|
| 76 | + -- | Creates an exception without a message. |
|---|
| 77 | + -- Default implementation is @'strMsg' \"\"@. |
|---|
| 78 | hunk ./Control/Monad/Error.hs 72 |
|---|
| 79 | + -- | Creates an exception with a message. |
|---|
| 80 | + -- Default implementation is 'noMsg'. |
|---|
| 81 | hunk ./Control/Monad/Error.hs 76 |
|---|
| 82 | - noMsg = strMsg "" |
|---|
| 83 | - strMsg _ = noMsg |
|---|
| 84 | +noMsg = strMsg "" |
|---|
| 85 | +strMsg _ = noMsg |
|---|
| 86 | hunk ./Control/Monad/Error.hs 79 |
|---|
| 87 | -instance Error [Char] where |
|---|
| 88 | +-- | A string can be thrown as an error. |
|---|
| 89 | +instance Error String where |
|---|
| 90 | hunk ./Control/Monad/Error.hs 87 |
|---|
| 91 | +{- | |
|---|
| 92 | +The strategy of combining computations that can throw exceptions |
|---|
| 93 | +by bypassing bound functions |
|---|
| 94 | +from the point an exception is thrown to the point that it is handled. |
|---|
| 95 | + |
|---|
| 96 | +Is parameterized over the type of error information and |
|---|
| 97 | +the monad type constructor. |
|---|
| 98 | +It is common to use @'Data.Either' String@ as the monad type constructor |
|---|
| 99 | +for an error monad in which error descriptions take the form of strings. |
|---|
| 100 | +In that case and many other common cases the resulting monad is already defined |
|---|
| 101 | +as an instance of the 'MonadError' class. |
|---|
| 102 | +You can also define your own error type and\/or use a monad type constructor |
|---|
| 103 | +other than @'Data.Either' String@ or @'Data.Either' IOError@. |
|---|
| 104 | +In these cases you will have to explicitly define instances of the 'Error' |
|---|
| 105 | +and\/or 'MonadError' classes. |
|---|
| 106 | +-} |
|---|
| 107 | hunk ./Control/Monad/Error.hs 104 |
|---|
| 108 | + -- | Is used within a monadic computation to begin exception processing. |
|---|
| 109 | hunk ./Control/Monad/Error.hs 106 |
|---|
| 110 | - catchError :: m a -> (e -> m a) -> m a |
|---|
| 111 | + |
|---|
| 112 | + {- | |
|---|
| 113 | + A handler function to handle previous errors and return to normal execution. |
|---|
| 114 | + A common idiom is: |
|---|
| 115 | + |
|---|
| 116 | + > do { action1; action2; action3 } `catchError` handler |
|---|
| 117 | + |
|---|
| 118 | + where the @action@ functions can call 'throwError'. |
|---|
| 119 | + Note that @handler@ and the do-block must have the same return type. |
|---|
| 120 | + -} |
|---|
| 121 | + catchError :: m a -> (e -> m a) -> m a |
|---|
| 122 | hunk ./Control/Monad/Error.hs 152 |
|---|
| 123 | --- --------------------------------------------------------------------------- |
|---|
| 124 | --- Our parameterizable error monad, with an inner monad |
|---|
| 125 | +{- | |
|---|
| 126 | +The error monad transformer. It can be used to add error handling to other |
|---|
| 127 | +monads. |
|---|
| 128 | hunk ./Control/Monad/Error.hs 156 |
|---|
| 129 | -newtype ErrorT e m a = ErrorT { runErrorT :: m (Either e a) } |
|---|
| 130 | +The @ErrorT@ Monad structure is parameterized over two things: |
|---|
| 131 | + |
|---|
| 132 | + * e - The error type. |
|---|
| 133 | + |
|---|
| 134 | + * m - The inner monad. |
|---|
| 135 | + |
|---|
| 136 | +Here are some examples of use: |
|---|
| 137 | hunk ./Control/Monad/Error.hs 164 |
|---|
| 138 | --- The ErrorT Monad structure is parameterized over two things: |
|---|
| 139 | --- * e - The error type. |
|---|
| 140 | --- * m - The inner monad. |
|---|
| 141 | +> -- wraps IO action that can throw an error e |
|---|
| 142 | +> type ErrorWithIO e a = ErrorT e IO a |
|---|
| 143 | +> ==> ErrorT (IO (Either e a)) |
|---|
| 144 | +> |
|---|
| 145 | +> -- IO monad wrapped in StateT inside of ErrorT |
|---|
| 146 | +> type ErrorAndStateWithIO e s a = ErrorT e (StateT s IO) a |
|---|
| 147 | +> ==> ErrorT (StateT s IO (Either e a)) |
|---|
| 148 | +> ==> ErrorT (StateT (s -> IO (Either e a,s))) |
|---|
| 149 | +-} |
|---|
| 150 | + |
|---|
| 151 | +newtype ErrorT e m a = ErrorT { runErrorT :: m (Either e a) } |
|---|
| 152 | hunk ./Control/Monad/Error.hs 176 |
|---|
| 153 | --- Here are some examples of use: |
|---|
| 154 | --- |
|---|
| 155 | --- type ErrorWithIO e a = ErrorT e IO a |
|---|
| 156 | --- ==> ErrorT (IO (Either e a)) |
|---|
| 157 | --- |
|---|
| 158 | --- type ErrorAndStateWithIO e s a = ErrorT e (StateT s IO) a |
|---|
| 159 | --- ==> ErrorT (StateT s IO (Either e a)) |
|---|
| 160 | --- ==> ErrorT (StateT (s -> IO (Either e a,s))) |
|---|
| 161 | --- |
|---|
| 162 | hunk ./Control/Monad/Error.hs 274 |
|---|
| 163 | +{- $customErrorExample |
|---|
| 164 | +Here is an example that demonstrates the use of a custom 'Error' data type with |
|---|
| 165 | +the 'ErrorMonad'\'s 'throwError' and 'catchError' exception mechanism. |
|---|
| 166 | +The example throws an exception if the user enters an empty string |
|---|
| 167 | +or a string longer than 5 characters. Otherwise it prints length of the string. |
|---|
| 168 | + |
|---|
| 169 | +>-- This is the type to represent length calculation error. |
|---|
| 170 | +>data LengthError = EmptyString -- Entered string was empty. |
|---|
| 171 | +> | StringTooLong Int -- A string is longer than 5 characters. |
|---|
| 172 | +> -- Records a length of the string. |
|---|
| 173 | +> | OtherError String -- Other error, stores the problem description. |
|---|
| 174 | +> |
|---|
| 175 | +>-- We make LengthError an instance of the Error class |
|---|
| 176 | +>-- to be able to throw it as an exception. |
|---|
| 177 | +>instance Error LengthError where |
|---|
| 178 | +> noMsg = OtherError "A String Error!" |
|---|
| 179 | +> strMsg s = OtherError s |
|---|
| 180 | +> |
|---|
| 181 | +>-- Converts LengthError to a readable message. |
|---|
| 182 | +>instance Show LengthError where |
|---|
| 183 | +> show EmptyString = "The string was empty!" |
|---|
| 184 | +> show (StringTooLong len) = |
|---|
| 185 | +> "The length of the string (" ++ (show len) ++ ") is bigger than 5!" |
|---|
| 186 | +> show (OtherError msg) = msg |
|---|
| 187 | +> |
|---|
| 188 | +>-- For our monad type constructor, we use Either LengthError |
|---|
| 189 | +>-- which represents failure using Left LengthError |
|---|
| 190 | +>-- or a successful result of type a using Right a. |
|---|
| 191 | +>type LengthMonad = Either LengthError |
|---|
| 192 | +> |
|---|
| 193 | +>main = do |
|---|
| 194 | +> putStrLn "Please enter a string:" |
|---|
| 195 | +> s <- getLine |
|---|
| 196 | +> reportResult (calculateLength s) |
|---|
| 197 | +> |
|---|
| 198 | +>-- Wraps length calculation to catch the errors. |
|---|
| 199 | +>-- Returns either length of the string or an error. |
|---|
| 200 | +>calculateLength :: String -> LengthMonad Int |
|---|
| 201 | +>calculateLength s = (calculateLengthOrFail s) `catchError` Left |
|---|
| 202 | +> |
|---|
| 203 | +>-- Attempts to calculate length and throws an error if the provided string is |
|---|
| 204 | +>-- empty or longer than 5 characters. |
|---|
| 205 | +>-- The processing is done in Either monad. |
|---|
| 206 | +>calculateLengthOrFail :: String -> LengthMonad Int |
|---|
| 207 | +>calculateLengthOrFail [] = throwError EmptyString |
|---|
| 208 | +>calculateLengthOrFail s | len > 5 = throwError (StringTooLong len) |
|---|
| 209 | +> | otherwise = return len |
|---|
| 210 | +> where len = length s |
|---|
| 211 | +> |
|---|
| 212 | +>-- Prints result of the string length calculation. |
|---|
| 213 | +>reportResult :: LengthMonad Int -> IO () |
|---|
| 214 | +>reportResult (Right len) = putStrLn ("The length of the string is " ++ (show len)) |
|---|
| 215 | +>reportResult (Left e) = putStrLn ("Length calculation failed with error: " ++ (show e)) |
|---|
| 216 | +-} |
|---|
| 217 | + |
|---|
| 218 | +{- $ErrorTExample |
|---|
| 219 | +@'ErrorT'@ monad transformer can be used to add error handling to another monad. |
|---|
| 220 | +Here is an example how to combine it with an @IO@ monad: |
|---|
| 221 | + |
|---|
| 222 | +>import Control.Monad.Error |
|---|
| 223 | +> |
|---|
| 224 | +>-- An IO monad which can return String failure. |
|---|
| 225 | +>-- It is convenient to define the monad type of the combined monad, |
|---|
| 226 | +>-- especially if we combine more monad transformers. |
|---|
| 227 | +>type LengthMonad = ErrorT String IO |
|---|
| 228 | +> |
|---|
| 229 | +>main = do |
|---|
| 230 | +> -- runErrorT removes the ErrorT wrapper |
|---|
| 231 | +> r <- runErrorT calculateLength |
|---|
| 232 | +> reportResult r |
|---|
| 233 | +> |
|---|
| 234 | +>-- Asks user for a non-empty string and returns its length. |
|---|
| 235 | +>-- Throws an error if user enters an empty string. |
|---|
| 236 | +>calculateLength :: LengthMonad Int |
|---|
| 237 | +>calculateLength = do |
|---|
| 238 | +> -- all the IO operations have to be lifted to the IO monad in the monad stack |
|---|
| 239 | +> liftIO $ putStrLn "Please enter a non-empty string: " |
|---|
| 240 | +> s <- liftIO getLine |
|---|
| 241 | +> if null s |
|---|
| 242 | +> then throwError "The string was empty!" |
|---|
| 243 | +> else return $ length s |
|---|
| 244 | +> |
|---|
| 245 | +>-- Prints result of the string length calculation. |
|---|
| 246 | +>reportResult :: Either String Int -> IO () |
|---|
| 247 | +>reportResult (Right len) = putStrLn ("The length of the string is " ++ (show len)) |
|---|
| 248 | +>reportResult (Left e) = putStrLn ("Length calculation failed with error: " ++ (show e)) |
|---|
| 249 | +-} |
|---|
| 250 | + |
|---|
| 251 | } |
|---|
| 252 | |
|---|
| 253 | Context: |
|---|
| 254 | |
|---|
| 255 | [Added Haddock documentation. Converted the module documentation to Haddock format. Per Jeff Newbern's permission included parts his tutorial "All About Monads" http://www.nomaware.com/monads/. |
|---|
| 256 | Andriy Palamarchuk <apa3a@yahoo.com>**20061218165621] |
|---|
| 257 | [add boilerplate Setup.hs |
|---|
| 258 | Ross Paterson <ross@soi.city.ac.uk>**20060928231525] |
|---|
| 259 | [use Control.Monad.Instances from base |
|---|
| 260 | Ross Paterson <ross@soi.city.ac.uk>**20060410112533] |
|---|
| 261 | [Add -fallow-undecidable-instances to some Control.Monad modules |
|---|
| 262 | simonpj@microsoft.com**20060209121334 |
|---|
| 263 | |
|---|
| 264 | I have recently tightened up GHC's implementation of the coverage |
|---|
| 265 | condition. As a result some of the Control.Monad modules are rejected. |
|---|
| 266 | |
|---|
| 267 | Example: |
|---|
| 268 | class (Monad m) => MonadReader r m | m -> r where |
|---|
| 269 | instance (Monoid w, MonadReader r m) => MonadReader r (WriterT w m) |
|---|
| 270 | Here, fv(Writer w m) is not a superset of fv(r). |
|---|
| 271 | |
|---|
| 272 | The flag allows it. I wonder if it's possible to use these modules |
|---|
| 273 | to send the type cheker into a loop. |
|---|
| 274 | |
|---|
| 275 | |
|---|
| 276 | ] |
|---|
| 277 | [TAG Initial conversion from CVS complete |
|---|
| 278 | John Goerzen <jgoerzen@complete.org>**20060112154134] |
|---|
| 279 | Patch bundle hash: |
|---|
| 280 | c6c1462ef1551bf85f5e505aaafbf26177a1c14b |
|---|