问题
Lets say I have a function
f :: State [Int] Int
and a function:
g :: StateT [Int] IO Int
I want to use f
in g
and pass the state between them. Is there a library function forStateT (return . runState f)
? Or in general, given a monad transformer with a corresponding monad, is there a library function for it?
回答1:
In even more general, what you're trying to do is apply a transformation to an inner layer of a transformer stack. For two arbitrary monads, the type signature might look something like this:
fmapMT :: (MonadTrans t, Monad m1, Monad m2) => (m1 a -> m2 a) -> t m1 a -> t m2 a
Basically a higher-level fmap
. In fact, it would probably make even more sense to combine it with a map over the final parameter as well:
fmapMT :: (MonadTrans t, Monad m1, Monad m2) => (m1 a -> m2 b) -> t m1 a -> t m2 b
Clearly this isn't going to be possible in all cases, though when the "source" monad is Identity
it's likely to be easier, but I can imagine defining another type class for the places it does work. I don't think there's anything like this in the typical monad transformer libraries; however, some browsing on hackage turns up something very similar in the Monatron package:
class MonadT t => FMonadT t where
tmap' :: FunctorD m -> FunctorD n -> (a -> b)
-> (forall x. m x -> n x) -> t m a -> t n b
tmap :: (FMonadT t, Functor m, Functor n) => (forall b. m b -> n b)
-> t m a -> t n a
tmap = tmap' functor functor id
In the signature for tmap'
, the FunctorD
types are basically ad-hoc implementations of fmap
instead of using Functor
instances directly.
Also, for two Functor-like type constructors F and G, a function with a type like (forall a. F a -> G a)
describes a natural transformation from F to G. There's quite possibly another implementation of the transformer map that you want somewhere in the category-extras
package but I'm not sure what the category-theoretic version of a monad transformer would be so I don't know what it might be called.
Since tmap
requires only a Functor
instance (which any Monad
must have) and a natural transformation, and any Monad
has a natural transformation from the Identity
monad provided by return
, the function you want can be written generically for any instance of FMonadT
as tmap (return . runIdentity)
--assuming the "basic" monad is defined as a synonym for the transformer applied to Identity
, at any rate, which is generally the case with transformer libraries.
Getting back to your specific example, note that Monatron does indeed have an instance of FMonadT
for StateT
.
回答2:
Such a function is not definable for all monad transformers. The Cont r
monad, for example, can't be lifted into ContT r IO
because that would require turning a continuation in the IO monad (a -> IO r
) into a pure continuation (a -> r
).
回答3:
What you are asking for is a mapping (known as a monad morphism) from a monad StateT m
to StateT n
. I'll be using the the mmorph library, which provides a very nice set of tools for working with monad morphisms.
To perform the State -> StateT m
transform you are looking for, we'll start by defining a morphism to generalize the Identity
monad embedded in State
,
generalize :: Monad m => Identity a -> m a
generalize = return . runIdentity
Next we'll want to lift this morphism to act on the inner monad of your StateT
. That is, we want a function which given a mapping from one monad to another (e.g. our generalize
morphism), will give us a function acting on the base monad of a monad transformer, e.g. t Identity a -> t m a
. You'll find this resembles the hoist
function of mmorph
's MFunctor
class,
hoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b
Putting the pieces together,
myAction :: State s Int
myAction = return 2
myAction' :: Monad m => StateT s m Int
myAction' = hoist generalize myAction
来源:https://stackoverflow.com/questions/4138671/combining-statet-and-state-monads