MonadError and fundeps

Marcin 'Qrczak' Kowalczyk qrczak@knm.org.pl
Fri, 11 May 2001 15:30:05 +0200 (CEST)


On Fri, 11 May 2001, Lauri Alanko wrote:

> Yep, but in hugs +o the latter overrides the first one. Which is quite
> convenient.

I doubt that it works predictably in all cases (when state types are not
known statically). I can try to construct an example if you wish.

> translift :: (MonadTrans t, Monad m, Monad (t m)) 
> 	       => (m a -> m b) -> t m a -> t m b
> 	       
> translift f m = m >>= lift . f . return
> 
> instance (MonadTrans t, MonadReader r m, Monad (t m)) 
>     => MonadReader r (t m) where
>         ask = lift ask
> 	local = translift . local
> 
> instance (MonadTrans t, MonadWriter w m, Monad (t m), Monoid w) =>
>     MonadWriter w (t m) where
>         tell = lift . tell
> 	listen = translift listen
> 	pass = translift pass

This gives wrong results (but I haven't checked). For example
    listen :: Monoid w
           => ReaderT r (Writer w) a -> ReaderT r (Writer w) (a, w)
doesn't listen what the action tells, but listens to 'return' which always
tells mempty. Similarly 'local' first runs the action in the original
environment and then provides a new environment to 'return' which doesn't
look at it.

I did most monad transformer forwarding instances in ghc-5.00 and hope
that I got them right, but I haven't tested them much. It's not that
mechanical (except MonadState), and some combinations can't be done at
all.

It could be advantageous to put something like translift in an extension
of MonadTrans. AFAIR many liftings of this type are similar (but the
function must be provided separately for each state transformer), so it
would simplify making forwarding instances.

> Is it inconceivable that m might actually have multiple ParsingStates,
> and thus you really have to specify which one you want to use to get
> the input?

The idea is to use a single state and abstract over the way in which
interesting components are contained in it. It has these advantages:
* It works. I doubt that automatic recognition of the state type would work.
* It allows to have multiple components of the same type in the state.

Now I see that my simulation of a fundep without the fundep (an extra
class which generates the dependency, instantiated separately for each
monad transformer, with MonadError as a superclass) doesn't work that
well: throwError would still be ambiguous so it needs a wrapper with a
type which tells how to determine the error type using the new class.
So I'm now convinced that MonadError should have the fundep too.

Some other mechanism could be invented to make it easier to embed various
components in the same type (for MonadReader & MonadState) or various
alternatives (for MonadError). I have a rather heavy proposal for the
first case (a language extension which redesigns records). OCaml has
a dual mechanism for the second (polymorphic variants). If my records
succeed, I will try to cover variants too.

-- 
Marcin 'Qrczak' Kowalczyk