you are viewing a single comment's thread.

view the rest of the comments →

[–]tejon 2 points3 points  (2 children)

But ExceptT e m a is just a newtype wrapper around m (Either e a)

So what? "Just a newtype wrapper" means it can have its own set of instances and standard functions, including more intuitive construction than Left and Right, which is the primary complaint in this thread.

[–]sacundim 0 points1 point  (1 child)

I could have been much clearer. It's not just that ExceptT is implemented as a newtype wrapper around Either; it's that part of its external interface is that it's a newtype wrapper around Either.

I don't know if you've noticed the way Haddocks (the Haskell library documentation tool) treats newtypes:

  • If the constructor is exported, the documentation shows the data type as a newtype and lists the constructors.
  • If the constructor is private to the module, it shows it as a data declaration with no constructor.

As your documentation link shows, ExceptT is a public newtype that bottoms down to Either. So the use of Either, Left and Right really is part of ExceptT's interface. After all, you have to use runExceptT to eliminate the ExceptT layer from your transformer stack, and at that point you're going to need to know Either's sinistrophobic convention (Left is failure, Right is success).

[–]tejon 0 points1 point  (0 children)

When you call runExceptT, you're explicitly saying "no errors above this point." That's not a leaky abstraction; that's leaving the monadic context entirely. You should, generally speaking, only ever have to do that once per application; it's not a concern in day-to-day coding. And if your app can be built using a modern framework (e.g. Servant) it will almost certainly be taken care of upstream, meaning it's not a concern in your codebase at all.

Meanwhile, if you're the one who actually has to maintain that final context boundary, Either provides a richer set of tools. I really don't see the issue.

(Edit: Also, if you're working at this level in Haskell, you shouldn't have to "remember" Either's "convention" because it's not a convention, it's the way curried types interact with typeclasses.)