开发者

What is the name of this Monad Stack function?

开发者 https://www.devze.com 2023-03-05 00:04 出处:网络
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:

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:

wr开发者_StackOverflow中文版apST :: (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.


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号