Benjamin Kovach
Benjamin Kovach

Reputation: 3260

Defining a MonadEither type class

I'm going back through Monad Transformers : Step by Step as a refresher, and like many tutorials out there, it uses Control.Monad.Error. GHC now gives a warning that this module is deprecated, so I switched over to Control.Monad.Trans.Either from the either library: http://hackage.haskell.org/package/either-3.4/docs/Control-Monad-Trans-Either.html

Everything is handled smoothly with eval2 in the paper, since EitherT is the outermost monad. However, after that everything falls apart -- ReaderT is in no way an Either value, and everything henceforth uses ErrorT, which I'd like to change to EitherT.

My idea, then, was to define a MonadEither type class that boxed left and right in order to handle errors, but this hasn't been fruitful. I don't really understand how the type classes in mtl work, and this instance in particular has to be parameterized over multiple values, which is confusing. I came up with the following, which compiles after including some syntactic extensions:

class (Monad m) => MonadEither l r m | m -> r where
  right :: r -> m r
  left  :: l -> m r

But I can't figure out a MonadEither instance of EitherT:

instance Monad m => MonadEither l r (E.EitherT l m) where
  right = E.right
  left  = E.left

Edit: I changed the instance declaration to match E.EitherT properly, and get the following error message:

Illegal instance declaration for ‘MonadEither l r (E.EitherT l m)’
  The coverage condition fails in class ‘MonadEither’
    for functional dependency: ‘m -> r’
  Reason: lhs type ‘E.EitherT l m’ does not determine rhs type ‘r’
In the instance declaration for ‘MonadEither l r (E.EitherT l m)’ 

Again, I'm not really sure what I'm doing. I don't really understand functional dependencies, so I'm just looking for some guidance as to what an appropriate MonadEither type class might look like, if possible to define.

Upvotes: 4

Views: 203

Answers (1)

Tom Ellis
Tom Ellis

Reputation: 9414

How about

instance Monad m => MonadEither l r (E.EitherT l m)

That is, it should be l instead of r.

However once you've done this you'll come up across a separate error. The root cause is that there's no point to right; it's just return. This means you need to get rid of the r parameter to the class.

class Monad m => MonadEither l m where
    left  :: l -> m a

Your instance declaration should then become

instance Monad m => MonadEither l (E.EitherT l m)

(You also may want to look at the MonadError class since this is essentially what you are replicating.)

Upvotes: 4

Related Questions