7
votes

I've got a bunch of stateful functions inside a State monad. At one point in the program there needs to be some IO actions so I've wrapped IO inside a StateT getting a pair of types like this:

mostfunctions :: State Sometype a
toplevel :: StateT Sometype IO a

To keep things simple I don't want pass the IO context into the main set of functions and I would like to avoid wrapping them in the monad stack type. But in order to call them from the toplevel function I need something akin to a lift, but I'm not trying to lift a value from the inner monad. Rather I want to convert the state in the StateT monad into something equivalent in the State monad. To do this I've got the following:

wrapST :: (State Sometype a) -> StateT Sometype IO a
wrapST f = do s <- get
              let (r,s2) = runState f s 
              put s2
              return r

This then get used to interleave things like the following:

toplevel = do liftIO $ Some IO functions
              wrapST $ Some state mutations
              liftIO $ More IO functions
              ....

It seems like a fairly obvious block of code so I'm wondering does this function have a standard name, and it is already implemented somewhere in the standard libraries? I've tried to keep the description simple but obviously this extends to pulling one transformer out of a stack, converting the wrapped value to the cousin of the transformer type, skipping the monads below in the stack, and then pushing the results back in at the end.

2
Can't you use the fact, that type State = StateT Identity? Use StateT Sometype m a for the big bunch of functions, so you can run them in both StateT IO and State.fuz

2 Answers

9
votes

It may be a good idea to refactor your code to use the type StateT SomeType m a instead of State SomeType a, because the first one is compatible to an arbitrary monad stack. If you'd change it like this, you don't need a function wrapST anymore, since you can call the stateful functions directly.

Okay. Suppose you have a function subOne :: Monad m => State Int Int:

subOne = do a <- get
            put $ a - 1
            return a

Now, change the types of all functions like this one from State SomeType a to StateT SomeType m a, leaving m as is. This way, your functions can work on any monadic stack. For those functions, that require IO, you can specify, that the monad at the bottom must be IO:

printState :: MonadIO m => StateT Int m ()
printState = do a <- get
             liftIO $ print a

Now, it should be possible to use both functions together:

-- You could use me without IO as well!
subOne :: Monad m => StateT Int m ()
subOne = do a <- get
            put $ a - 1

printState :: MonadIO m => StateT Int m ()
printState = do a <- get
             liftIO $ print a

toZero :: StateT Int IO ()
toZero = do subOne     -- A really pure function
            printState -- function may perform IO
            a <- get
            when (a > 0) toZero

PS: I use GHC 7, some of the libs changed midway, so it might be a bit different on GHC 6.

3
votes

A more direct answer to your question: the function hoist does exactly what you're describing in a slightly more generic way. Example usage:

import Control.Monad.State
import Data.Functor.Identity
import Control.Monad.Morph

foo :: State Int Integer
foo = put 1 >> return 1

bar :: StateT Int IO Integer
bar = hoist (return . runIdentity) foo

hoist is part of the MFunctor class, which is defined like this:

class MFunctor t where
  hoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b

There are instances for most monad tranformers, but not ContT.