Fedor Gogolev
Fedor Gogolev

Reputation: 10541

Extensible errors

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?

Upvotes: 1

Views: 123

Answers (1)

Ankur
Ankur

Reputation: 33637

You can try something like this where each algo has its own error sum type.

{-# LANGUAGE TypeFamilies #-}

class Algorithm a where
    data CanFailWith a :: *
    check :: a -> [Int] -> Maybe (CanFailWith a)

data FirstAlgo = FirstAlgo deriving (Show)

instance Algorithm FirstAlgo where
    data CanFailWith FirstAlgo = FError1 |  FError2
    check a x = Nothing

data SecondAlgo = SecondAlgo deriving (Show)

instance Algorithm SecondAlgo where
    data CanFailWith SecondAlgo = SError1 | SError2 | SError3
    check a x = Nothing


testError :: CanFailWith SecondAlgo -> ()
testError SError1 = ()
testError SError2 = ()
testError SError3 = ()

Upvotes: 2

Related Questions