monad transformer library (was: Re: list monad transformer)

Andrew J Bromage ajb@spamcop.net
Wed, 21 May 2003 14:20:09 +1000


G'day all.

On Tue, May 20, 2003 at 10:10:24AM -0700, Iavor Diatchki wrote:

> actually i disagree with this quite strongly :-) ideally i want no lifts 
> in my program. things like: lift $ lift $ lift $ raise "error" are, 
> well, annoying.

Of course you want no lifts in your program.  What you should have
written instead is something like this:

	myLiftToError = lift . lift . lift

	myRaise = myLiftToError . raise

I agree that client code which uses your particular stack of monad
transformers shouldn't have to know what order the transformers are
stacked in, but even more so, they shouldn't have to even know what
the transformers _are_.  If it's that complex, it should be abstract.

> now all the lifts need to be changed.

If the lift operations were abstract, you would only need to change
at most N of them, where N is the number of stacked transformers, and
even then it would only be in one place.

> and besides if one 
> has 1 of each transformer the whole "lift" thing is pointless as there 
> is no ambiguity as to what you mean (i.e. put clearly refers to the 
> state transformer wherevr it is).

Not if there's an RWS transformer somewhere else on the stack.  I
understood that the main reason for the typeclasses was that there
may be more than one reasonable implementation of a given interface.

> when there is more than one copy of a 
> transformer things are different.  then some sort of addressing is 
> necessary, which is what motivated adding the "indexes" to my library. 
> they work quite well, but i still think there must be a better way to 
> achieve the same effect...

I'm curious how these indexes work.  There are many situations where I
need a little extra state/nondeterminism/whatever for a small part of
my code, and for that, stacking on an extra transformer can do the
trick nicely.  Does this upset the scheme that you use for indexing?

> unfortunatelly i can't quite capture this commonality with a single 
> instance.  perhaps (as bellow) overlapping instances (in some form) 
> could help.

Overlapping instances may help reduce the quadratic complexity, and if
it could be done right (i.e. if the Haskell implementation can be
trusted to pick the correct instance), I might be happy with that as a
compromise.  That way I can just do this:

	instance (MonadNondet m, MonadTrans t) => MonadNondet (t m) where
		msolutions = lift . msolutions
		mcommit = lift . mcommit

and trust that the highest MonadNondet instance is picked.

> actually the library can be generalized there. in my library i have a class:
> 
> class (Monad m, Monad n) => HasBase m n | m -> n where
>   inBase :: n a -> m a

That's an excellent idea.

As a random thought, there may be an argument for incorporating stToIO
and/or ioToST in here too.

Cheers,
Andrew Bromage