Within the Prelude we define CanAbort and ActionFail typeclasses with similar definitions. When should we use one versus the other?
ActionFail
exists to desugar pattern matches in do-notation desugar and not really anything else, e.g.,
Some x <- y
I woulnd’t really recommend writing anything that is polymorphic in ActionFail
or calling fail
yourself.
CanAbort
on the other hand is intended for user-defined code and the standard library and so on.
Writing code polymorphic in that is perfectly reasonable.
So while the two are equivalent on the technical level they differ in how they are and are intended to be used.
Thank you. Would you be opposed if I added this information to the docs?
Not at all, that would be great!
To follow up a bit on this thread, does this mean that ideally the Total
modules in the DA stdlib (ex. DA.List.Total) should have used CanAbort
instead?
What’s also the right verb here? Does a function “use” a typeclass or “implement” it?
That’s an interesting question @Leonid_Rozenberg. Looking at the code in DA.List.Total, it seems like these functions all use fail
to report pattern match failures, so to me ActionFail
is appropriate. I think that still agrees with @cocreature’s recommendation against “writing anything that is polymorphic in ActionFail
or calling fail
yourself” since this isn’t “yourself” writing these functions.
Regarding your other question, I’d say “use” (I did above!). I’d reserve “implement” for instance
s, e.g. map
is the implementation of fmap
for []
.
I see. My point though is that the fact that they use fail
is an implementation detail. For the purposes of programming with them it would be easier if their signature had CanAbort
instead as I would want to use that typeclass in my potentially-failing code.
That’s a good point, any Action
-polymorphic code using these is going to inherit the ActionFail
constraint. With that argument, I agree that CanAbort
would be more appropriate. What do you think @cocreature?