I'm trying to make sense of one of the examples presented at the pipes tutorial concerning 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)
If the example above is run, we get output like the following:
>>> 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>
>>>
It seems that:
Select input
) will take turns (maybe non-deterministically) at reading the input. 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.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).I'm missing something fundamental about the way the example above works, but I cannot see what.
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
doesn't work that way. Instead, it is "depth-first". Every time it gets a first name, it starts reading the whole list of last names anew.
The example doesn't do that, but each list of last names could depend on the first name that has been read previously. Like this:
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.
If we are reading last names and encounter a quit command, that "branch" terminates and we go back to the level above, to read another first name from the list. The "effectful list" that reads the last names is only re-created after we have a first name to work with.
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).
Notice that entering a single quit at the very beginning will also terminate the program, as we are "closing" the top-level list of first names.
Basically, every time you enter a quit
you close the current branch and go up a level in the "search tree". Every time you enter a first name you go down one level.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With