I’ve got a set of users, groups, and a mapping between users and groups. I have various functions that manipulate these sets, however one should not be able to add a user<->group mapping for a user that does not exist, nor remove a group which still has users as members, etc.
So basically I want these functions to throw “exceptions” that must be explicitly dealt with by the caller.
I first thought of returning something like this:
data Return r e = Success r | Exception e
And if the caller fails to pattern match against the Exception, they’ll hopefully get a compiler warning, or at the very least have an obvious runtime error when there is a problem.
Is this the best approach, and is there a pre-packaged solution that does this? Note I need to throw and catch “exceptions” in pure code, not the IO Monad.
Yes, this is a good approach, and it’s in the standard library:
Return r eis the same asEither e r. You can even write code like you would using exceptions inIO, too (i.e. without having to explicitly handle the errors at each step with pattern matching): theMonadinstance forEitherpropagates the errors, just like theMaybemonad does (but with the additionalevalue in the case of an error). For example:If
mightFail aormightFail breturnsLeft someError, thenfoo a b cwill, too; the errors are automatically propagated. (Here,throwErroris just a nice way of writingLeft, using the functions fromControl.Monad.Error; there’s alsocatchErrorto catch these exceptions.)