Describe a problem in several words. Guess I have an algorithms to check something, and depending on algorithm it can fail with slightly different errors, for example:
data Error1 = Error1
data Error2 = Error2
data Error3 = Error3
class Algorithm a where
type CanFailWith a :: *
check :: Something -> Maybe (CanFailWith a)
instance Algorithm FirstAlgorithm where
type CanFailWith FirstAlgorithm = Either Error1 Error2
...
instance Algorithm SecondAlgorithm where
type CanFailWith SecondAlgorithm = Either Error1 (Either Error2 Error3)
...
This option is not very friendly to user because of difficulty to work with it's branchy structure, e.g.
testError (Left Error1) = ...
testError (Right (Left Error2)) = ...
testError (Right (Right Error3)) = ...
It looks not so bad with three errors, but it worth with every extra error.
Error could be a simple sum type:
data Error = Error1
| Error2
| Error3
But in this case I force user to cover impossible case in first algorithm, that can not fail with Error3
A question is: is there any common and desirable simple to end user solutions for extending errors?
Eithers slightly easier to handle usingTypeOperatorslikedata a :+: b = a :+: bwith, say,infixr 6 :+:. Now you can writeinstance Algorithm SecondAlgorithm where type CanFailWith SecondAlgorithm = Error1 :+: Error2 :+: Error3. There's probably a tricky-typed way of making a generalized eliminator function for such a type, but I can't think of it right now.testError? You need to decide upon the type of this function firsttestError :: CanFailWith SecondAlgorithm -> ...in this example, but it not necessary to be exactly same.