tag:blogger.com,1999:blog-1777990983847811806.post5611564277991379745..comments2024-03-16T16:29:29.582-07:00Comments on Haskell for all: errors-1.0: Simplified error handlingGabriella Gonzalezhttp://www.blogger.com/profile/01917800488530923694noreply@blogger.comBlogger24125tag:blogger.com,1999:blog-1777990983847811806.post-86216605426162178862017-12-14T08:47:10.676-08:002017-12-14T08:47:10.676-08:00You're welcome! :)You're welcome! :)Gabriella Gonzalezhttps://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-35412821277552036592017-12-14T08:39:58.213-08:002017-12-14T08:39:58.213-08:00Gabriel, thank you very much!!!Gabriel, thank you very much!!!Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-46439849164027384872017-12-14T08:37:44.395-08:002017-12-14T08:37:44.395-08:00Yeah, the `exceptions` package will behave the exa...Yeah, the `exceptions` package will behave the exact same way. The `MonadCatch` instance for `Either` will only catch errors represented as `Left` values but not pure errors.Gabriella Gonzalezhttps://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-50736438458392776522017-12-14T08:35:42.027-08:002017-12-14T08:35:42.027-08:00So, with Control.Monad.Catch - the reason is the s...So, with Control.Monad.Catch - the reason is the same? It's in package exceptions, and there is such signature: `(~) * e SomeException => MonadCatch (Either e)`, so I decided that it will catch any errors (SomeException) and to translate them to Either. But it does not work tooAnonymousnoreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-77284563551573290732017-12-14T08:25:09.149-08:002017-12-14T08:25:09.149-08:00The only way to catch a pure error like that is to...The only way to catch a pure error like that is to use the `spoon` library:<br /><br />https://hackage.haskell.org/package/spoon<br /><br />`catchEither` won't do that, because the following equation will always hold no matter what:<br /><br />Right x `catchEither` f = Right x<br /><br />... no matter what `x` and `f` are, so in your case:<br /><br /> (Right $ toEnum $ fromEnum b) `catchEither` Left = Right $ toEnum $ fromEnum b Gabriella Gonzalezhttps://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-52672635085951626732017-12-14T08:14:10.245-08:002017-12-14T08:14:10.245-08:00Hello, Gabriel! I try to catch error's excepti...Hello, Gabriel! I try to catch error's exception with catchEither, but w/o success:<br /><br /> safeConv = (Right $ toEnum $ fromEnum b) `catchEither` Left<br /><br />Exception is leaking and not catching. What's wrong with this code? I only want to catch exception w/o to involve IO and to keep it pureAnonymousnoreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-50607640819659695922016-01-05T09:01:59.414-08:002016-01-05T09:01:59.414-08:00If there are better options now (ExceptT?), it wou...If there are better options now (ExceptT?), it would be nice to mention it on top of the blogpostNicolashttps://www.blogger.com/profile/03434148024010872285noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-62566099484555889982013-08-06T11:57:23.360-07:002013-08-06T11:57:23.360-07:00By `transformers` API I mean `lift`ing everything ...By `transformers` API I mean `lift`ing everything manually. The way you avoid using the auto-lifting machinery is only import modules from the `transformers` library. That means that instead of `Control.Monad.Error` you would import `Control.Monad.Trans.Error` and instead of `Control.Monad.Trans` you would import `Control.Monad.Trans.Class`.Gabriella Gonzalezhttps://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-15150602189848364892013-08-06T11:51:26.667-07:002013-08-06T11:51:26.667-07:00by 'transformers API' you mean avoiding th...by 'transformers API' you mean avoiding the use of MonadTrans and do the lifting manually?<br /><br />How do you avoid using the "auto-lifting" machinery? By not using functions from the Monad* typeclasses? (In our example, using left instead of throwError). Because even the trasformers from the transformer library have MonadTrans instance declarations, so you'll get autolifted as soon as you use any of the other Monad* typeclasses...<br /><br />(At this point I'm just curious. I'm way too lazy to lift everything!)Riccardo Pelizzihttps://www.blogger.com/profile/07735194013529688266noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-32268208621330079322013-08-06T11:00:18.484-07:002013-08-06T11:00:18.484-07:00I usually advise beginners to stick to the `transf...I usually advise beginners to stick to the `transformers` API until they feel comfortable with it. It also leads to better type errors and type inference. It is a little more verbose, but the reward is easier to maintain code.<br /><br />Also, if you have a large function that has lots of deeply nested lifts, you can save a lot of time by declaring these at the top of your function:<br /><br /> liftState = id<br /> liftReader = lift<br /> liftEither = lift . lift<br /> ...<br /><br />Then if you later change your monad transformer stack for that function, all you have to do is change those top-level lift definitions and everything still works.<br /><br />Another trick is to define your own custom monad transformer newtype that auto-lifts the relevant operations for you. That way you define all the machinery in one place and if you need to modify your monad transformer stack you just change the code in one place and every other use is automatically fixed. However, this is only worth doing if you use the exact same monad transformer stack all over your code base. Think of it as defining your own custom DSL. Haskell lets you pick and choose which language features you build into your DSL (i.e. errors, state, etc.).Gabriella Gonzalezhttps://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-59886749594240354462013-08-06T10:55:32.889-07:002013-08-06T10:55:32.889-07:00it works, thanks! it seems like I have to go back ...it works, thanks! it seems like I have to go back and understand lifting, because anytime the MonadTrans-auto-lifting-goodness breaks I can't do it myself :-)Riccardo Pelizzihttps://www.blogger.com/profile/07735194013529688266noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-55739505057647712472013-08-06T10:27:52.153-07:002013-08-06T10:27:52.153-07:00Both `transformers` and `mtl` are interchangeable ...Both `transformers` and `mtl` are interchangeable and compatible with each other. `mtl` itself depends on `transformers` and gets its monad transformers from there. `mtl` just adds on the type classes like `MonadError`. So mixing the two packages is completely safe.<br /><br />If you are doing that, then make sure you import `Control.Monad.Error.Class` which provides the type-classed `throwError` function that you don't have to `lift`. Alternatively, just add two `lift`s in front of your `left` and it should also work.Gabriella Gonzalezhttps://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-53335637045338550482013-08-06T08:46:08.119-07:002013-08-06T08:46:08.119-07:00It looks like I was using mtl for all my stuff (in...It looks like I was using mtl for all my stuff (including ErrorT), but your Control.Errors package imports transformers. Does mixing the two create problems? It seems that there is no EitherT in mtl...<br /><br />The list is used as a regular return type here, not as a nondeterministic monad. I get the right interpretation in ErrorTRiccardo Pelizzihttps://www.blogger.com/profile/07735194013529688266noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-57148737025046742522013-08-06T08:36:19.189-07:002013-08-06T08:36:19.189-07:00Are you using the `mtl` or `transformers` (in othe...Are you using the `mtl` or `transformers` (in other words, did you import `Control.Monad.Error` or `Control.Monad.Trans.Error`)? If you are using the `mtl`, just don't substitute the `throwError` function: it is type-classed to work with any monad transformer stack that has `ErrorT` or `EitherT` in it (it works with both). If you are using `transformers`, then just add two `lift`s before it like this:<br /><br /> lift $ lift $ left ...<br /><br />However, judging from your type error that you pasted it looks like you made a completely different mistake during the refactoring. The type error says that you are trying to lift a list as a monadic action where it expects an `EitherT` action. That's the only part I don't understand. I'd have to see the whole source code for your function to say more.Gabriella Gonzalezhttps://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-27482897617025000912013-08-06T08:27:56.360-07:002013-08-06T08:27:56.360-07:00you are right, it does. But substituting
ErrorT -&...you are right, it does. But substituting<br />ErrorT -> EitherT<br />runErrorT -> runEitherT<br />throwError -> left<br />doesn't work.<br /><br />the type error seems to imply that the, unlike 'throwError', the call to 'left' does not lift the value into the monad stack, but instead creates a new EitherT at the bottom of the stack.<br /><br />Couldn't match type `EitherT EvalError m0 a0' with `[SValue]'<br /> Expected type: EvalMonad [SValue]<br /> Actual type: StateT<br /> Permission<br /> (ReaderT EvalContext (EitherT EvalError Identity))<br /> (EitherT EvalError m0 a0)<br /> In the expression: throwError $ TypeError s<br /> In an equation for `toList': toList s _ = throwError $ TypeError s<br /><br />n.b. i aliased left to throwError, and EvalMonad is aliased to StateT-ReaderT-EitherT-Identity<br /><br />The definition of left seems to be indeed analogous to 'throwError'. But then what is missing? Riccardo Pelizzihttps://www.blogger.com/profile/07735194013529688266noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-33129293320482489252013-08-05T14:07:45.397-07:002013-08-05T14:07:45.397-07:00You can stack it. It has a `MonadError` instance....You can stack it. It has a `MonadError` instance. The only thing you should have to change is to substitute `ErrorT` for `EitherT`. Or are you referring to something else?Gabriella Gonzalezhttps://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-6811717817321192732013-08-05T14:06:09.675-07:002013-08-05T14:06:09.675-07:00I just wasted hours trying to switch from ErrorT t...I just wasted hours trying to switch from ErrorT to EitherT to clean up a 3-line spurious Error instance declaration, only to realize I'd have to paste and adapt tons of difficult code I barely understand to get the same functionality. There should really be a big warning somewhere in the EitherT documentation, because the resulting type error is not helpful for a beginner.<br /><br />I am actually having trouble understanding the purpose of a transformer that you can't stack...Riccardo Pelizzihttps://www.blogger.com/profile/07735194013529688266noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-20523665879664226532012-12-07T09:29:52.906-08:002012-12-07T09:29:52.906-08:00Great! For now I've just written the instance ...Great! For now I've just written the instance myself, but you're right that it requires `UndecidableInstances`. Nothing has gone wrong so far, though, but I'll look forward to your post.Adam Foltzerhttps://www.blogger.com/profile/02349610069687732099noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-41878268339821650982012-12-06T10:12:39.207-08:002012-12-06T10:12:39.207-08:00That's correct. I will soon update the post t...That's correct. I will soon update the post to reflect that.<br /><br />I switched to "tryIO"'s new behavior due to feedback from user's of the library who preferred a combinator that only caught synchronous exceptions, and preserved the old behavior as "scriptIO".Gabriella Gonzalezhttps://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-66615081869048052822012-12-06T10:11:07.506-08:002012-12-06T10:11:07.506-08:00I'm not the author of the `either` library, bu...I'm not the author of the `either` library, but my guess is that this is for one of two reasons:<br /><br />a) `either` is written in the style of `transformers` where you manually lift commands<br /><br />b) `either` is intended to be Haskell98, and writing `mtl` instances requires extensions like `UndecidableInstances`<br /><br />I generally prefer the `transformers` style over the `mtl` style. The `mtl` style does have some semantic problems, namely that the behavior changes depending on the order in which the monad transformers are applied.<br /><br />There is actually a principled way to do what the `mtl` does that lets you still type class monad transformer operations while still preserving the ability to reason about your code, and I will blog about that later.Gabriella Gonzalezhttps://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-44175037584596082522012-12-06T10:04:04.793-08:002012-12-06T10:04:04.793-08:00I've just started using this package, and am w...I've just started using this package, and am wondering if there's a fundamental reason why there's no `MonadState` instance for `EitherT`. Is it safe to just write one myself, or is this left out because it has some semantic problems?Adam Foltzerhttps://www.blogger.com/profile/02349610069687732099noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-59704373510583408972012-12-05T22:01:55.308-08:002012-12-05T22:01:55.308-08:00It seems with version 1.3 onwards, the "tryIO...It seems with version 1.3 onwards, the "tryIO" function mentioned here is now "scriptIO". And, "tryIO" is now used to catch an IOException and convert it directly to an EitherAnonymoushttps://www.blogger.com/profile/17573013499050680407noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-76043797199776668652012-07-09T09:02:04.402-07:002012-07-09T09:02:04.402-07:00This is a good point. Technically, the Script mon...This is a good point. Technically, the Script monad was intended to be a quick solution for very simple programs, which is why it specifies a concrete monad transformer stack and uses Strings to hold exceptional values. However, that seems like a common enough use case to warrant including in its behavior. E-mail me at my gmail address with username Gabriel439 and, if you can, show me the modified runScript function you have in mind.Gabriella Gonzalezhttps://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-4291872989446659002012-07-09T02:02:31.340-07:002012-07-09T02:02:31.340-07:00Thanks for this package, it seems very nice!
In t...Thanks for this package, it seems very nice!<br /><br />In the Script monad I think you really shouldn't catch AsyncExceptions.<br /><br />Example code in a server:<br /><br />servingThread = forever $ serve<br /><br />And the serve function is written with your Script monad. When the whole server wants to quit, the mainThread will obviously call killThread servingThreadId. Although, this async exception will be caught by the Script monad and nicely handled as if it was something related to the request (unable to parse, wrong values, etc.).<br /><br />More on this topic:<br />http://hackage.haskell.org/packages/archive/base/latest/doc/html/Control-Exception.html#g:4<br /><br />Simon Marlow's tutorial also contains info about handling async exceptions in the right way: community.haskell.org/~simonmar/par-tutorial.pdfGergely Riskohttps://www.blogger.com/profile/17683945755215976255noreply@blogger.com