如何嵌套monad

时间:2013-12-03 12:03:37

标签: haskell monads

我在Haskell中有这个代码:

import Control.Monad.Trans.State

simpleState = state (\x -> (x, x + 1))

runUntil :: (s -> Bool) -> State s a -> State s a
runUntil f s = do
    s' <- get
    -- Here I want to print value of s' to console
    if f s'
        then s >> runUntil f s
        else s

main :: IO ()
main = do
    let (x,s) = runState (runUntil (< 10) simpleState) 0
    putStrLn $ "State = " ++ (show s) ++ " Result = " ++ (show x)

我想在runUntil的每次迭代中打印状态值 如果我无法在runUntil函数中打印它,我可以这样做吗?

2 个答案:

答案 0 :(得分:11)

欢迎来到Monad变形金刚的精彩世界。有一个很好的库叫MTL,提供大多数monad的“monad transformer”等价物。按照惯例,这些以大写字母T结尾,所以StateT就是我们想要的。 Monad变换器有他们常用的操作,还有一个lift用于StateT,看起来像这样,

lift :: Monad m => m a -> StateT s m a

现在IO上有一个名为MonadIO的变形金刚专门课程。要使用它,我们会做类似的事情。它类似于一个普通的旧monad变换器,但具有类型签名

liftIO :: (MonadIO m, Monad m) => IO a -> m a

 import Control.Monad.State
 import Control.Monad.Trans

 simpleState :: StateT Integer IO ()
 simpleState = modify (+1)

 runUntil :: Show s => (s -> Bool) -> StateT s IO a -> StateT s IO s
 runUntil pred newState = do
    curr <- get
    if pred curr
    then liftIO (print curr) >> newState >> runUntil pred newState
    else return curr

然后运行它,有一组方便的函数可以将StateT s m a转换为s -> (s, a)

main :: IO ()
main = do
  (x,s) <- runStateT (runUntil (< 10) simpleState) 0
  putStrLn $ "State = " ++ (show s) ++ " Result = " ++ (show x)

请注意,现在我们使用bind(<-),因为结果在IO中,它不再是纯粹的。 Monad变形金刚可能很混乱,幸运的是Real World Haskell有一章关于它们。如果你感到困惑,那就值得一看。

答案 1 :(得分:3)

在我看来,我们的打印可能仅用于调试......

如果我是正确的,您可以使用Debug.trace从任何地方(甚至在IO功能之外)打印到终端。

只需导入Debug并连接到任何其他值,如

trace (show s') $ 
    if f s'
        then s >> runUntil f s
        else s

(注意跟踪的签名是String-&gt; a-&gt; a,所以你需要$后跟另一个表达式

同样,这仅适用于开发代码,应该从生产代码中删除(它完全打破了整个“无副作用”的事情)!