Search code examples
haskellmonadsmonad-transformersstate-monad

What is the name of this Monad Stack function?


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.


Solution

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