Stacking up state transformers

Andrew J Bromage ajb@spamcop.net
Wed, 5 Feb 2003 15:55:21 +1100


G'day.

On Tue, Feb 04, 2003 at 05:24:29PM -0000, Guest, Simon wrote:

> I can still access my backtracked state using Control.Monad.State.{get,put}, but
> I can't access my non-backtracked state.

Iavor mentioned using "lift", plus some other ideas.  That's what
I'd do:

	liftNondet = lift
	liftOuterState = lift . lift

(Naturally I'd call these something more meaningful.)

As a matter of style, I generally advocate the philosophy that your
basic operations should be semantically meaningful, rather than
operationally meaningful.  So, for example, rather than:

	type FooM a = StateT Bar (StateT Baz IO) a

	munch :: FooM ()
	munch = do baz <- lift (lift get)
		   doStuffWith baz

I prefer:

	type FooM a = StateT Bar (StateT Baz IO) a

	getBaz :: FooM Baz
	getBaz = lift (lift get)

	munch :: FooM ()
	munch = do baz <- getBaz
		   doStuffWith baz

Not only is it more readable, it's also more robust in the face of
change (e.g. when you decide to change to ReaderT instead).

In your case, it's a state monad you're trying to get at, and a state
monad only supports a few meaningful operations (get, put, modify, gets)
not all of which are generally useful for a given kind of state.  I
think it makes more sense to specify a "public interface" for your
monad and use only that.

Cheers,
Andrew Bromage