To abort or to fail

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 instances, 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?

1 Like