What purpose does the complexity of `Except` serve in Haskell?

痞子三分冷 提交于 2019-12-05 18:00:02

Abstract

Use Either for normal success/error APIs. It's defined in the base library, so it doesn't push other dependencies on a consumer. Also, this is one of the most basic Haskell types, so 'everyone' understands how it works.

Only use ExceptT if you specifically need to combine Either with another monad (such as, for example IO). This type is defined in the transformers library, so pushes an extra dependency on consumers. Additionally, monad transformers is a more advanced feature of Haskell, so you can't expect everyone to understand how to use it.

Speculation on reasons

I wasn't around when those decisions were made, but it seems that there are various historical reasons for the confusion. Haskell is an old language (older than Java!), so even though efforts have been made to streamline it and rectify old mistakes, some still remain. As far as I can tell, the Either/ExceptT confusion is one of those situations.

I'm speculating that Either is older than the concept of monad transformers, so I imagine that the type Either was introduced to the base library early in the history of Haskell.

The same thing seems to be the case with Maybe.

Other monads, likes e.g. Reader and State seem to have been introduced (or at least 'retconned') together with their monad transformers. For example, Reader is just a special case of ReaderT, where the 'other' Monad is Identity:

type Reader r = ReaderT r Identity

The same goes for StateT:

type State s = StateT s Identity

That's the general pattern for many of the monads defined in the transformers library. ExceptT just follows the pattern by defining Except as the special case of ExceptT.

There are exceptions to that pattern. For example, MaybeT doesn't define Maybe as a special case. Again, I believe that this is for historical reasons; Maybe was probably around long before anyone started work on the transformers library.

The story about Either seems even more convoluted. As far as I can tell, there was, originally, an EitherT monad transformer, but apparently (I forget the details) there was something wrong with the way that it behaved (it probably broke some laws), so it was replaced with another transformer called ErrorT, which again turned out to be wrong. Third time's the charm, I suppose, so ExceptT was introduced.

The Control.Monad.Trans.Except module follows the pattern of most other monad transformers by defining the 'uneffectful' special case using a type alias:

type Except e = ExceptT e Identity

I suppose it does that because it can, but it may be unfortunate, because it's confusing. There's definitely prior art that suggests that a monad transformer doesn't have to follow that pattern (e.g. MaybeT), so I think it would have been better if the module hadn't done that, but it does, and that's where we are.

I would essentially ignore the Except type and use Either instead, but use ExceptT if a transformer is required.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!