管道教程:ListT示例

Dam*_*les 6 haskell haskell-pipes

我试图弄清楚管道教程中提到的一个例子ListT:

import Pipes
import qualified Pipes.Prelude as P

input :: Producer String IO ()
input = P.stdinLn >-> P.takeWhile (/= "quit")

name :: ListT IO String
name = do
    firstName <- Select input
    lastName  <- Select input
    return (firstName ++ " " ++ lastName)
Run Code Online (Sandbox Code Playgroud)

如果运行上面的示例,我们得到如下输出:

>>> runEffect $ every name >-> P.stdoutLn
Daniel<Enter>
Fischer<Enter>
Daniel Fischer
Wagner<Enter>
Daniel Wagner
quit<Enter>
Donald<Enter>
Stewart<Enter>
Donald Stewart
Duck<Enter>
Donald Duck
quit<Enter>
quit<Enter>
>>> 
Run Code Online (Sandbox Code Playgroud)

看起来:

  1. 当你运行它(在ghci上)时,你输入的第一个名字将被绑定,只有第二个名称会改变.我希望两个生产者(定义为Select input)在读取输入时轮流(可能是非确定性的).
  2. 输入quit一次将允许重新绑定第一个名称.同样,我没有看到为什么firstName会绑定到用户输入的第一个值.
  3. quit连续输入两次将终止该程序.但是,我希望quit只需要输入两次才能退出程序(可能与其他输入交替).

我遗漏了上面例子的工作原理,但是我看不清楚.

dan*_*iaz 9

当你运行它(在GHCi上)时,你输入的第一个名字将被绑定,只有第二个名称会改变.我希望两个生产者(定义为Select input)在读取输入时轮流(可能是非确定性的).

ListT不这样做.相反,它是"深度优先".每次获得名字时,它都会重新开始阅读整个姓氏列表.

该示例不会这样做,但每个姓氏列表可能取决于先前已读取的名字.像这样:

input' :: String -> Producer String IO ()
input' msg = 
    (forever $ do 
        liftIO $ putStr msg
        r <- liftIO $ getLine
        yield r
    ) >-> P.takeWhile (/= "quit")

name' :: ListT IO String
name' = do
    firstName <- Select input
    lastName  <- Select $ input' $ "Enter a last name for " ++ firstName ++ ": "
    return (firstName ++ " " ++ lastName)
Run Code Online (Sandbox Code Playgroud)

输入一次退出将允许重新绑定第一个名称.同样,我不明白为什么firstName将绑定到用户输入的第一个值.

如果我们正在阅读姓氏并遇到退出命令,那么"分支"终止,我们回到上面的级别,从列表中读取另一个名字.只有在使用名字后,才会重新创建读取姓氏的"有效列表".

quit连续输入两次将终止该程序.但是,我希望quit只需要输入两次才能退出程序(可能与其他输入交替).

请注意,在最开始输入单个退出也将终止程序,因为我们正在"关闭"顶级名字列表.

基本上,每次进入时,quit您都会关闭当前分支并在"搜索树"中上升一级.每次输入名字时,您都会进入一个级别.

  • 旁注:与`ListT`不同,`free`包的模块`Control.Monad.Trans.Iter`中的"迭代monad变换器"可以让你使用`MonadPlus`实例组合"并行"的有效动作列表.http://hackage.haskell.org/package/free-4.12.4/docs/Control-Monad-Trans-Iter.html https://gist.github.com/danidiaz/e9382127d1c4025b9845 (2认同)