What are the difference in usage between Either and Except in Haskell?
Asked Answered
C

2

7

I have a class that can be created from several arguments in Haskell which requires some complex validation of those arguments. Currently I have something like

makeAThingExcept :: String -> String -> ... String -> Except ThingError AThing
makeAThingExcept s1 s2 ... = do
    unless (s1CheckPasses s1) (throwError (BadS1 s1))
    ...

data ThingError = BadS1 String ...

instance Show ThingError where
        show (BadS1 s) = "Bad S1: " ++ s

makeAThing :: String -> String -> ... String -> AThing
makeAThing s1 s2 ... = case runExcept (makeAThingExcept s1 s2 ...) of
        Right thing  -> thing
        Left err -> error (show err)

Setting aside whether there is a better way to do this by using more specific types than String as arguments to makeAThingExcept, is there a reason to prefer Except over Either in a case like this? What are the differences here between the capabilities and idiom of Except vs Either?

Chainey answered 7/11, 2018 at 14:31 Comment(6)
It probably comes down what type class instances are defined for Except; you can convert between Except and Either using except and runExcept.Glynnis
@Glynnis Ah, so there's some fluidity there, wrt. tuning Except vs Either depending on context.Chainey
Here in my package, FWIW, getting a valid AThing is really essential, and entering valid args can be a bit tricky. So failure from user input (e.g. from a typo) can be common; but in code, something would have had to go quite wrong with whatever was generating the arguments: code that supplied bad arguments would be doing a bad job of making AThings.Chainey
So, naively, the former case is like an "either" or even a "maybe" (it could happen fix it and try again), while the latter is closer to an "exception" (your code isn't specifying AThings correctly).Chainey
You'll have to wait for someone who actually knows to provide an answer :) I think the only real difference is that the name ExceptT was introduced to provide a type that was semantically devoted to error handling, as opposed to Either which is the generic way to define sum types, only one application of which is to provide error-handling semantics.Glynnis
@chepner: How do I import except (or for that mater ExceptT as a constructor). I can't seem to make an Except from an Either.Chainey
O
2

As noted in the comments, it's easy to convert between Except & Either. The runtime representation is the same, even.

I would always choose to use Either. It's ubiquitous in libraries. I very rarely see Except.

Except is a special case of ExceptT, which you will see in libraries. If you find yourself writing a lot of functions with Reader SomeType (Either e a) or IO (Either e a) or Monad m => m (Either e a), then you might want to consider ExceptT. It's fine not to worry about it until then - Either is easier to use until it isn't.

Ormand answered 7/11, 2018 at 15:44 Comment(1)
I have a follow up question.Chainey
H
6

The difference is in the instance of Alternative. The "base" package does not export any for Either, as I understand because the authors didn't want to introduce a bias towards any of the values, which on itself is because Either is supposed to be a general sum-type, and representing the possibility of errors is only a special case. However the "transformers" package does provide an orphan instance, which binds it to the Error class:

Error e => Alternative (Either e)

However the community has never accepted either the typeclass or the orphan instance, which is why it is now deprecated. IOW you can look at it as if Either still has no instance of Alternative.

The Except type does have a non-orphan instance, which doesn't even bind the user to any made-up classes, but Monoid instead:

(Monad m, Monoid e) => Alternative (ExceptT e m)

It is also worth noting, that for cases when dealing with validation and accumulating all errors, the Validation abstraction is more appropriate. There is a tradeoff though: it cannot be a monad, but it is a selective (between applicative and monad) and an alternative.

Heavyduty answered 30/1, 2019 at 8:15 Comment(0)
O
2

As noted in the comments, it's easy to convert between Except & Either. The runtime representation is the same, even.

I would always choose to use Either. It's ubiquitous in libraries. I very rarely see Except.

Except is a special case of ExceptT, which you will see in libraries. If you find yourself writing a lot of functions with Reader SomeType (Either e a) or IO (Either e a) or Monad m => m (Either e a), then you might want to consider ExceptT. It's fine not to worry about it until then - Either is easier to use until it isn't.

Ormand answered 7/11, 2018 at 15:44 Comment(1)
I have a follow up question.Chainey

© 2022 - 2024 — McMap. All rights reserved.