简体   繁体   English

如何嵌套monad

[英]How to nest monads

I have this code in Haskell: 我在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)

I want to print the value of the state on each iteration of runUntil. 我想在runUntil的每次迭代中打印状态的值。
If I can't print it in runUntil function where I can do this? 如果我无法在runUntil函数中打印它,我可以这样做吗?

Welcome to the wonderful world of Monad Transformers. 欢迎来到Monad变形金刚的精彩世界。 There's a nice library called the MTL that provides the "monad transformer" equivalents of most monads. 有一个很好的库叫MTL,提供大多数monad的“monad transformer”等价物。 By convention, these end with a capital T, so StateT is what we want. 按照惯例,这些以大写StateT T结尾,因此StateT就是我们想要的。 Monad transformers have their usual operations and one more, lift , for a StateT that looks like this, Monad变压器有他们惯常的操作,还有一个, lift ,对于看起来像这样的StateT

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

Now there's a special class for transformers on top of IO called MonadIO . 现在,在IO之上有一个特殊的变换器类叫做MonadIO To use it, we'd do something like. 要使用它,我们会做类似的事情。 It's similar to just a plain old monad transformer but has the type signature 它类似于一个普通的旧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

Then to run it, there's a handy set of functions that turn StateT sma 's into s -> (s, a) . 然后运行它,有一套方便的函数可以将StateT sma变成s -> (s, a)

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

Notice that now we use bind (the <- ) because the result is in IO , it's no longer pure. 请注意,现在我们使用bind( <- ),因为结果是在IO ,它不再是纯粹的。 Monad transformers can be pretty confusing, luckily Real World Haskell has a chapter on them. Monad变形金刚可能会让人感到困惑,幸运的是, Real World Haskell有一章介绍它们。 If you're confused it's worth looking at. 如果你感到困惑,那值得一看。

It looks to me like our print might just be for debugging purposes.... 在我看来,我们的打印可能仅用于调试目的....

If I am correct, you can print to the terminal from anywhere (even outside of an IO function) using Debug.trace. 如果我是正确的,您可以使用Debug.trace从任何地方(甚至在IO功能之外)打印到终端。

Just import Debug and connect to any other value like this 只需导入Debug并连接到这样的任何其他值

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

(note the signature of trace is String->a->a, so you need the $ followed by another expression (注意跟踪的签名是String-> a-> a,所以你需要$后跟另一个表达式

Again, this is only for development code and should be removed from production code (it totally breaks the whole "no side effects" thing)! 同样,这仅适用于开发代码,应该从生产代码中删除(它完全打破了整个“无副作用”的事情)!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM