19

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 for
StateT (return . runState f)? Or in general, given a monad transformer with a corresponding monad, is there a library function for it?

Don Stewart
  • 137,316
  • 36
  • 365
  • 468
HaskellElephant
  • 9,819
  • 4
  • 38
  • 67
  • I believe that TomMD's edit is incorrect. I believe that the original `g :: StateT [Int] IO Int` should stand. – glguy Nov 10 '10 at 06:59
  • I liked the other changes, fixed the parenthesis ... – HaskellElephant Nov 10 '10 at 10:24
  • 2
    This question seems like what I'm looking for, but the answers are all much more complicated than http://stackoverflow.com/questions/17325485/combining-statet-io-with-state, which got the job done for me. – crockeea Sep 29 '13 at 19:39

3 Answers3

5

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
bgamari
  • 5,913
  • 1
  • 18
  • 21
5

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.

C. A. McCann
  • 76,893
  • 19
  • 209
  • 302
  • I haven't looked at the Monatron package. I'll have to look closer at that to judge it. I do like your idea of defining a type class for when it does work, can anyone confirm or disconfirm that Monatron does this? – HaskellElephant Nov 09 '10 at 21:56
4

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).

Heatsink
  • 504
  • 2
  • 3
  • Didn't think of that. Like you said, this isn't possible for every monadtransformer. So it would require a special kind of connection between the transformer and the corresponding monad then... – HaskellElephant Nov 09 '10 at 21:50
  • Wouldn't the transformation go the other way? Since the `r` in a continuation type is usually polymorphic, you could just write `(ContT . runCont) :: Cont (m r) a -> ContT r m a`. – C. A. McCann Nov 09 '10 at 22:00
  • @camccann I was going for a generalized class of transformations (Monad m => m a -> (TransformerOf m) m' a) (abusing notation a bit). If you try to write an instance for (Cont r a -> ContT r m a), you'll get stuck at the point I described. – Heatsink Nov 09 '10 at 22:50