[Haskell] monad transformers

Iavor Diatchki iavor.diatchki at gmail.com
Mon Jan 31 14:54:13 EST 2005


(appologies --- as I was writing, my post digressed from your original point, 
I hope the discussion is still interesting though :-)

On Sat, 29 Jan 2005 18:48:00 -0800, John Meacham <john at repetae.net> wrote:
> ...
> instance (Monad m, Monad (t m), MonadTrans t, MonadStats m) => MonadStats (t m) where
>     mticks' n k = lift $ mticks' n k
> the first, however, is tricky. Right now, it appears that every Monad
> type in the libraries defines a rule for commuting with every other
> monad type, this seems impracticle for many monads (n^2 rules!) so, my
> question is, why arn't the standard monad transformers declared with an
> instance like above?

The library defines the interaction of every transformer with every other one.
If you think of monad transformers as "language features", we are
simply saying how each features interacts with every other one (this
is why we have to be careful when we introduce new language features
:-).  Not all features are orthogonal (i.e. they don't all commute). 
In my experience, the features that don't commute are the ones that
somehow tend to affect the control flow of the program, e.g. exception
handling, backtracking, explicit continuations.

Now for many transformers the interaction with other ones is "straight
forward" and can be captured with a declaration like the above.  Such
declarations lead to overlapping instances,
usually you need to rules, e.g.
instance ReaderM (ReaderT r m) r where ...
instance (ReaderM m r, Trans t) => ReaderM (t m) r where ...

The overlapping instances are nice in this case, as they cut down on
the code a lot.
However the interaction of overlapping instances and functional
dependencies is a bit unclear (at least in my mind).  This leads to
the question" "Should functional dependencies be used?".
I have heard arguments both for, and against using them.
The question is "can a monad be a reader in more than one way?".
Clearly we can make such moands (use ReaderT twice).  But then, when
we say "Get the environment", which one do we mean?  With functional
dependencies the answer (usually) is:   get the outermost environment,
and you have to do something special (explicit lifting) to access the
other ones.
Without fun.deps. the answer is: try to figure out which one we mean,
based on the expected type of the value.
Often that leads to ambiguities, but of course that can be fixed by
writing types.

Neither of the approaches is particularly satisfying: the point is
that if we are going to have more than one of something, then
different things should have names, so that we can specify whcih one
we mean.   With fun. deps. we only have a single name (the putermost
one), without them, we are using the types of things for their names.
Both of these are unsatisfactory.  It is possible in Haskell to code
up a name like system --- I wrote a library like that once, and it is
on my web page (www.cse.ogi.edu/~diatchki).  It involves the (by now
usual) class hackery and it is not real pretty.  Essentially, classes
get another parameter (the name of the layer), and there is functional
dependenciy between the monad and the name, but not the type, e.g. you
get constraints like:
ReaderM m x t
saying: "the monad 'm' has a read-only field called 'x' of type 't'".
Sadly you cannot pick arbitrary names for your variables, instead the
variable names are natural numbers.  Note that now the functional
dependency makes sense --- it says that we should not have variables
with the same name, but we can have multple variables of the same
type.   A language feature avoiding such hackery would be nice.
By the way, note that having multiple layers of the same monad is
really useful sometimes
and the layers cannot be colapsed into a single layer with a tuple. For example:
StateT s1 (ExceptT err1 (StateT s2 m))
We cannot colapse the two state layers into a single one.  Raising an
'err1' error undoes the changes to state variable 's1', but not the
changes to variable 's2'.

> ** The monad transformer libraries appear to have moved somewhere in the
>    most recent cvs fptools tree, anyone know where they moved too?
I am not sure where "the standard" library is. There are some
transformers under:
libraries/monads.    They are very similar to the ones in the standard library.
The ones in CVS are my working version, there is a link to a "stable"
one (yeah right :-) on my web page (monadLib), and the next realease
should appear there sometime this week
(maybe even today).

More information about the Haskell mailing list