instance (MonadState s m, MonadTrans t, Monad (t m)) => MonadState s (t m) where

get = lift get

put = lift . put

and the boilerplate is gone. The surprising thing is that you can do the same for other operations. I showed above the simplest example that I know of, which is for ask and local, but the same technique can be applied to other operations. There is a general technique which allows you to determine the appropriate monad in which catchError is algebraic (catchError is not algebraic with the current semantics). This same technique yields State as the appropriate monad for local.

]]>The example of appendL is more compelling.

What about other operations? I’m guessing that throwError is algebraic and catchError is not.

]]>Also, the monad morphism from [] into another monad is exactly what you get with any monad transformer. For example,

lift :: [a] -> StateT s [a].

]]>local’ f m = get >>= \s -> set (f s) >> m

I don’t mean to be negative, but I’m not seeing how this technique makes anything simpler. Defining a state reader in terms of a state transformer is already trivial, and defining a morphism from [] into a given monad seems at least as complicated as defining mzero and mplus directly.

]]>reallocal is not algebraic, and hence, not liftable. However, it is actually defined for any monad which supports

`ask'`

and `local'`

and those are liftable.
Sorry for all the confusion.

]]>Dave: As you say, the correct functor for local is the pair

S a = (r->r, a), and of course local is the curried version of

S (Reader r a) -> Reader r a.

The function `local'`

is algebraic, and its semantics will be preserved by the liftings However, as you mention, the semantics of local’ are not the same as those of local (I admit the name is totally misleading). You can get the semantics of the old local in the following manner:

reallocal :: (r->r) -> Reader r a -> Reader r a

reallocal f m = do

old < - ask

a <- local f m

local (const old) (return a)

h (>>= (>>=f)) = h . fmap (>>=(>>=f))

or equivalently

join . h = h . fmap join

(I’m going to edit it in the main post)

]]>Second, local’ doesn’t seem to act like local. If I understand the given definition, then in local’ f e1 >>= e2, the changed environment created by f will be visible in e1 and e2, instead of just e1.

]]>h >=> f = h . fmap (>>= f)

]]>