管道教程:ListT 示例
Pipes Tutorial: ListT example
我正在尝试理解 pipes tutorial 中关于 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)
如果上面的例子是运行,我们得到如下输出:
>>> 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>
>>>
好像是:
- 当你 运行 这个(在 ghci 上)时,你输入的名字将被绑定,只有第二个会改变。我希望两个生产者(由
Select input
定义)将轮流(可能不确定地)读取输入。
- 输入一次
quit
将允许重新绑定名字。同样,我不明白为什么 firstName
会绑定到用户输入的第一个值。
- 连续输入
quit
两次将终止程序。但是,我希望 quit
只需输入两次即可退出程序(可能与其他输入交替)。
关于上面示例的工作方式,我遗漏了一些基本的东西,但我看不出是什么。
When you run this (on GHCi), the first name you input will get bound
and only the second will change. I would expect that both producers
(defined by Select input
) will take turns (maybe
non-deterministically) at reading the input.
ListT
这样不行。相反,它是 "depth-first"。每次获得名字时,它都会重新开始读取整个姓氏列表。
该示例没有这样做,但每个姓氏列表都可能取决于之前读取的名字。像这样:
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)
Entering quit one time will allow to re-bind the first name. Again, I
fail to see why firstName will get bound to the first value entered by
the user.
如果我们正在读取姓氏并遇到退出命令,则 "branch" 终止,我们返回到上面的级别,从列表中读取另一个名字。读取姓氏的 "effectful list" 只有在我们有了要使用的名字后才会重新创建。
Entering quit
twice in a row will terminate the program. However, I
would expect that quit
only has to be entered twice to quit the
program (possibly alternating with other input).
请注意,在最开始输入一个 quit 也会终止程序,因为我们是 "closing" 顶级名字列表。
基本上,每次输入 quit
时,您都会关闭当前分支并在 "search tree" 中上升一个级别。每输入一个名字,就会下降一级。
为了补充@danidiaz 的出色答案,您 可以 再次获得 name
提示 firstName
的行为,而不仅仅是继续询问lastName
s.
你可以做的是利用 Monad m => ListT m
的 MonadZip
实例,特别是
mzip :: Monad m => ListT m a -> ListT m b -> ListT m (a, b).
因此,您可以将 name
定义为
name :: ListT IO String
name = do
(firstName, lastName) <- mzip (Select input) (Select input)
return (firstName ++ " " ++ lastName)
你会得到你的交替行为。
另外,您还可以使用 MonadComprehensions
和 ParallelListComp
扩展。使用这些,name
的两个版本变为
name :: ListT IO String
name = [ firstName ++ " " ++ lastName | firstName <- Select input
, lastName <- Select input ]
name' :: ListT IO String
name' = [ firstName ++ " " ++ lastName | firstName <- Select input
| lastName <- Select input ]
我正在尝试理解 pipes tutorial 中关于 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)
如果上面的例子是运行,我们得到如下输出:
>>> 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>
>>>
好像是:
- 当你 运行 这个(在 ghci 上)时,你输入的名字将被绑定,只有第二个会改变。我希望两个生产者(由
Select input
定义)将轮流(可能不确定地)读取输入。 - 输入一次
quit
将允许重新绑定名字。同样,我不明白为什么firstName
会绑定到用户输入的第一个值。 - 连续输入
quit
两次将终止程序。但是,我希望quit
只需输入两次即可退出程序(可能与其他输入交替)。
关于上面示例的工作方式,我遗漏了一些基本的东西,但我看不出是什么。
When you run this (on GHCi), the first name you input will get bound and only the second will change. I would expect that both producers (defined by
Select input
) will take turns (maybe non-deterministically) at reading the input.
ListT
这样不行。相反,它是 "depth-first"。每次获得名字时,它都会重新开始读取整个姓氏列表。
该示例没有这样做,但每个姓氏列表都可能取决于之前读取的名字。像这样:
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)
Entering quit one time will allow to re-bind the first name. Again, I fail to see why firstName will get bound to the first value entered by the user.
如果我们正在读取姓氏并遇到退出命令,则 "branch" 终止,我们返回到上面的级别,从列表中读取另一个名字。读取姓氏的 "effectful list" 只有在我们有了要使用的名字后才会重新创建。
Entering
quit
twice in a row will terminate the program. However, I would expect thatquit
only has to be entered twice to quit the program (possibly alternating with other input).
请注意,在最开始输入一个 quit 也会终止程序,因为我们是 "closing" 顶级名字列表。
基本上,每次输入 quit
时,您都会关闭当前分支并在 "search tree" 中上升一个级别。每输入一个名字,就会下降一级。
为了补充@danidiaz 的出色答案,您 可以 再次获得 name
提示 firstName
的行为,而不仅仅是继续询问lastName
s.
你可以做的是利用 Monad m => ListT m
的 MonadZip
实例,特别是
mzip :: Monad m => ListT m a -> ListT m b -> ListT m (a, b).
因此,您可以将 name
定义为
name :: ListT IO String
name = do
(firstName, lastName) <- mzip (Select input) (Select input)
return (firstName ++ " " ++ lastName)
你会得到你的交替行为。
另外,您还可以使用 MonadComprehensions
和 ParallelListComp
扩展。使用这些,name
的两个版本变为
name :: ListT IO String
name = [ firstName ++ " " ++ lastName | firstName <- Select input
, lastName <- Select input ]
name' :: ListT IO String
name' = [ firstName ++ " " ++ lastName | firstName <- Select input
| lastName <- Select input ]