Haskell 使用ContT的列表迭代器

Haskell 使用ContT的列表迭代器,haskell,iterator,continuations,Haskell,Iterator,Continuations,我有一个简单的列表,我想在每个元素之间迭代“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

我有一个简单的列表,我想在每个元素之间迭代“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
我得到的是:

nil
感谢您的帮助

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已经很懒惰了,所以像这样的迭代模式通常只在资源管理问题需要与需求驱动的使用模式良好交互时使用。而且,您根本不需要延续单子:

它不是编写
K
构造函数来获取元组,而是更习惯于

data K a = Nil | K a (() -> K a)
直观地说,
listIterator
的类型没有使用它的一元结构:它只是构造一个值,所以

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符号来写

这可能不是您想要的答案,但是如果您对这种编程风格感兴趣,您应该研究
管道
和类似的库。(
conduct
是“现实世界”中的新星,但pipes提供了一个更简单的教学工具,这就是我在这里使用它的原因。)

管道类似于迭代器,只是它们有三种风格:可以获取输入(消费者)、生成输出(生产者)和同时执行这两种功能(管道)。如果连接管道,使输入端和输出端都满足要求,则称之为“管道”,它是一个独立的单元,无需任何额外输入即可运行

管道提供了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
导入控件.Pipe
导入控制.Monad.Trans.Class
导入控制.Monad.IO.Class
--令人烦恼的是,Pipe没有提供MonadIO实例
实例(MonadIO m)=>MonadIO(管道a b m),其中
升力,升力。liftIO
listIterator::Monad m=>[a]->Producer(可能是a)m()
listIterator(x:xs)=收益率(仅x)>>listIterator xs
listIterator[]=不产生任何结果
打印机::(MonadIO m,Show a)=>消费者(可能是a)m()
打印机=do
mx liftIO(打印x)>>打印机
Nothing->liftIO(putStrLn“nil”)
main=runPipe$listIterator[1,2,3]>+>打印机

这个列表非常简单,特别是如果你读过Gabriel最近关于免费单子的博客文章,尤其是和。

因为haskell很懒,你已经有了简单列表类型的延续优势(
[]
),但我想你这样做是为了学习。:)这与哈斯凯尔有什么关系吗?注意,在哈斯凯尔,由于懒惰和纯洁性,
()->a
a
是一样的。使用前者的唯一原因是,如果您使用的是需要函数而不是普通值的高阶函数。还要注意,编写这样的代码的标准方法是
mapM_uu.print[1,2,3]>>putStrLn“nil”
。感谢您的解释。希望你能看到我想做的,你能提供一些指导吗?作为未来访问者的注意事项:由于Haskell具有非严格的语义,
()->ka
实际上等同于
ka
,因此
ka(ka)
实际上就足够了。
$ cabal update && cabal install pipes
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