# Ticket #1059: error.diff

File error.diff, 10.7 KB (added by Andriy, 7 years ago) |
---|

Line | |
---|---|

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 |