Simple monads

Wolfgang Jeltsch wolfgang@jeltsch.net
Fri, 27 Jun 2003 19:27:36 +0200


On Thursday, 2003-06-26, 23:57, CEST, Derek Elkins wrote:
> [...]

> > not deeply understanding the use of Haskell extensions in the State
> > source,
>
> I'm assuming Control.Monad.State's source in which case -no- extensions a=
re
> used for -State- (well, at least I don't see any quickly glancing).
> Extensions are used for the -MonadState class-.  For the MonadState class
> they are pretty much necessary.  Multiparameter type classes are necessary
> because the state type depends on the monad (get would have type forall
> s.Monad m =3D> m -> s otherwise which is rather meaningless), the function
> dependencies tell the type checker that the state type is completely
> determined by the monad type.

Hello,

why not swap the state and the monad parameter of StateT? The definition wo=
uld=20
become something like the following:
    newtype StateT m s a =3D StateT (s -> m (a,s))
With this we could create a MonadState class which doesn't use type system=
=20
extensions. It could be defined like this:
    class MonadState m where
        get :: m s s
        put :: s -> m s ()
Note that m now has kind * -> * -> *. Note also that this restricts the=20
MonadState class because only state transformers which can work with every=
=20
state type are now possible as instances. But, at least, State and our=20
modified StateT can be instantiated without problem.

The problem arises when we try to make a MonadTrans instance for our new=20
StateT because MonadTrans needs a type of kind * -> * -> * whoose first=20
argument is a monad. But we can create a different MonadTrans class based o=
n=20
the kind of functional dependency usage we just dropped for MonadState. We=
=20
just write:
    class MonadTrans (Monad m, Monad tm) =3D> m tm | tm -> m where
        lift :: m a -> tm a
Instead of writing
    instance MonadTrans T where ...
we would now write
    instance Monad m =3D> MonadTrans m (T m) where ...
and for our new StateT type we would write
    instance Monad m =3D> MonadTrans m (StateT m s) where ...

The new MonadTrans class would be more powerful. This would have the nice=20
effect that we don't need MonadIO anymore. Instead of writing
    MonadIO m
we could just use
    MonadTrans IO m

Changing MonadTrans this way would help me with my parser module.=B9 I have=
 a=20
type Parser which needs three parameters, a "base monad", a token type and =
an=20
output type. The base monad parameter has the same purpose as the monad=20
parameter in ReaderT, WriterT, StateT etc. The lift function makes sense fo=
r=20
my parser type, so I want a MonadTrans instance. This would restrict me to=
=20
the parameter order
    token - base monad - output
which is rather unfortunate for me. The reason is that there are parser=20
functions which fulfill the arrow axioms. The arrow type is a parser applie=
d=20
to a specific base monad. So I want to write something like
    instance Monad baseMonad =3D> Arrow (Parser baseMonad) where ...
which implies that the base monad must be the first parameter.

This brings me to another point. One year ago we had a discussion on The=20
Haskell Mailing List concerning arrows. (The subject of the mails was just=
=20
"arrows".) The point was that it seemed strange to me that first and second=
=20
are included in the basic arrow class Arrow while left and right have their=
=20
extra class ArrowChoice. Not only that it seemed strange to me but it made =
it=20
impossible to make Parser baseMonad an instance of Arrow. Parser baseMonad=
=20
has nice implementations of pure and (>>>) but none of first or second.

Currently, I use my own Arrow module which provides an arrow class, that=20
doesn't include first and second. I'm really not happy with using a=20
replacement for a module from the hierarchical libraries. Is there any chan=
ce=20
of changing the class structure of Control.Arrow?

> [...]

Wolfgang

=B9 The parser module is part of Seaweed. It's the module Seaweed.Core.Pars=
ing.=20
The source code of Seaweed can be accessed via this URI:
    http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/seaweed/code/
There is also the module Seaweed.Core.Parsing.Utilities which provides seve=
ral=20
useful things implemented on top of the core parsing module.