我有一个简单的列表,我想迭代每个元素之间的“yield”并将该元素打印到输出。我正在尝试使用ContT monad执行此操作但遇到问题。这是我到目前为止所做的:
data K a = Nil | K (a,() -> K a)
listIterator :: (Monad m) => [r] -> m (K r)
listIterator [] = return Nil
listIterator (x:xs) = return (ContT (\k -> K (x,k))) >> listIterator xs
runIterator :: IO ()
runIterator = do
a <- listIterator ([1,2,3] :: [Int])
let loop Nil = liftIO $ print "nil"
loop (K (curr,newI)) =
do
liftIO $ print curr
loop (newI ())
loop a
预期输出为:
1
2
3
nil
我得到的是:
nil
感谢任何帮助!
答案 0 :(得分:4)
listIterator (x:xs) = return (ContT (\k -> K (x,k))) >> listIterator xs
没有做你期望的,等式推理
listIterator (x:xs)
= return (ContT (\k -> K (x,k))) >> listIterator xs
= (return (ContT (\k -> K (x,k)))) >>= \_ -> listIterator xs
= (\_ -> listIterator xs) (ContT (\k -> K (x,k)))
= listIterator xs
我不确定你为什么要使用迭代器。 Haskell已经很懒,所以这样的迭代模式主要用于只有资源管理问题需要与需求驱动的使用模式很好地交互时。并且,您根本不需要延续monad:
而不是编写K
构造函数来取一个元组,而不是
data K a = Nil | K a (() -> K a)
直观地说,listIterator
的类型不使用它的monadic结构:它只是构造一个值,所以
listIterator ::[r] -> K r
listIterator [] = Nil
listIterator (x:xs) = K x (\_ -> listIterator xs)
现在生活是微不足道的
runIterator :: IO ()
runIterator = do
let a = listIterator ([1,2,3] :: [Int])
loop Nil = liftIO $ print "nil"
loop (K curr newI) =
do
liftIO $ print curr
loop (newI ())
loop a
最好不要使用do notation来编写。
答案 1 :(得分:1)
这可能不是你想要的答案,但如果你对这种编程风格感兴趣,你应该研究pipes
和类似的库。 (conduit
是“现实世界”中的后起之秀,但是管道提供了一种更简单的教学工具,这就是我在这里使用它的原因。)
$ cabal update && cabal install pipes
管道就像迭代器一样,除了它们有三种形式:可以获取输入的那些(消费者),产生输出的那些(生产者)和那些两者(管道)。如果连接管道使得输入和输出端都满足,那么它被称为“管道”,它是一个独立的单元,可以在没有任何额外输入的情况下运行。
Pipe提供了一个monad实例,以方便创建管道。 >+>
运算符将两个管道连接在一起。
import Control.Pipe
import Control.Monad.Trans.Class
import Control.Monad.IO.Class
-- annoyingly, Pipe does not provide a MonadIO instance
instance (MonadIO m) => MonadIO (Pipe a b m) where
liftIO = lift . liftIO
listIterator :: Monad m => [a] -> Producer (Maybe a) m ()
listIterator (x:xs) = yield (Just x) >> listIterator xs
listIterator [] = yield Nothing
printer :: (MonadIO m, Show a) => Consumer (Maybe a) m ()
printer = do
mx <- await
case mx of
Just x -> liftIO (print x) >> printer
Nothing -> liftIO (putStrLn "nil")
main = runPipe $ listIterator [1, 2, 3] >+> printer
source for Control.Pipe非常简单,特别是如果你一直在阅读Gabriel最近关于免费monad的博文,特别是Why free monads matter和Purify code using free monads。