RE: "interact" behaves oddly if used interactively
For GHC (6.0.1)
"main=interact id" basically echoes every line of my input, whereas "main=interact show" correctly waits for EOF before outputting something.
Which of these are you claiming is wrong? This is the behaviour I'd expect in both cases (although I'd have to go and carefully review the code for 'show :: String -> String' in the report to be able to tell whether it was supposed to be that strict or not).
Furthermore the buffering mode must be "LineBuffering".
The buffering mode of a Handle attached to a terminal is LineBuffering by default.
If I explicitely set the buffering to "NoBuffering" I'm not able to enter EOF by typing "ctrl-d".
This is a consequence of Unixy terminal semantics. Unix does line buffering on terminals in the kernel as part of the terminal processing, unless you turn it off. However, the Ctrl-D processing is also part of the terminal processing which gets turned off when the kernel line buffering is disabled. So GHC tries its best to get NoBuffering semantics by turning off the kernel line buffering, but as a result you lose Ctrl-D. C'est la vie. I've added this to the FAQ.
Furthermore my terminal seems to remain in the buffering mode set by the previously called ghc-haskell program (because it effects programs that do not "hSetBuffering".)
This is a bug, recently fixed.
What should a student think about "interact" in the Prelude? (It's ok for pipes only, I guess.)
Well, combining laziness with I/O is always going to be problematic. I'd say steer clear of introducing students to interact. Cheers, Simon
"main=interact id" basically echoes every line of my input, whereas "main=interact show" correctly waits for EOF before outputting something.
Which of these are you claiming is wrong?
I guess "interact" does what it should, but I think it should be changed to avoid interleaved in- and output.
lose Ctrl-D. C'est la vie.
That is okay if I'm daring enough to change the buffering. Hugs and NHC gave me no chance for "ctrl-d", though.
I'd say steer clear of introducing students to interact.
Good advice (currently), but I thought, interact could be used for a simple stand alone program, that does not require "do" (or ">>="). Christian
Christian Maeder <maeder@tzi.de> writes:
I guess "interact" does what it should, but I think it should be changed to avoid interleaved in- and output.
Surely the name suggests that "interactive" behaviour is required, i.e. exactly some interleaving of input and output. The chunk-size of the interleaving should depend only on the strictness of the argument to "interact". Unfortunately, the visible behaviour can also currently depend on buffering and echoing of the input (and possibly also buffering of the output), which I feel is a mistake. Regards, Malcolm
Malcolm Wallace wrote: [...]
Surely the name suggests that "interactive" behaviour is required, i.e. exactly some interleaving of input and output. The chunk-size of the interleaving should depend only on the strictness of the argument to "interact".
I'm not happy that interleaving depends on the strictness. Lazy or strict evaluation should only change the behaviour of overall termination (lazy evaluation should terminate more often). I'ld rather implement interleaving (or "interactive" behaviour) explicitely by: interact f = do s <- getLine putStrLn (f s) interact f (assuming line buffering) (Terminating with "ctrl-c") Surely also something is needed for endless character resources as Tom pointed out. Christian
Christian Maeder <maeder@tzi.de> writes:
I'm not happy that interleaving depends on the strictness. Lazy or strict evaluation should only change the behaviour of overall termination (lazy evaluation should terminate more often).
But the whole purpose of 'interact' is to use its argument as the demanding function which drives lazy consumption of the input. It is *designed* to reveal the evaluation behaviour, by hoisting it into the I/O monad. (AFAIK, 'interact' was explicitly designed for beginners, as an easy way to turn a pure computation into one which will actually run, before they learn about the I/O monad. Anyone from the original Haskell committee should feel free to correct me at this point, but interact was never intended for "serious" users.)
I'ld rather implement interleaving (or "interactive" behaviour) explicitely by:
interact f = do s <- getLine putStrLn (f s) interact f
Your suggested implementation does not have anything like the same semantics as the current 'interact'. For instance, consider the following program to number the lines of its input: main = interact (unlines . zipWith lineno [0..] . lines) where lineno n s = shows n (" "++s) Your version of interact behaves very unintuitively here! Regards, Malcolm
Christian Maeder wrote:
Malcolm Wallace wrote: [...]
Surely the name suggests that "interactive" behaviour is required, i.e. exactly some interleaving of input and output. The chunk-size of the interleaving should depend only on the strictness of the argument to "interact".
I'm not happy that interleaving depends on the strictness. Lazy or strict evaluation should only change the behaviour of overall termination (lazy evaluation should terminate more often). I'ld rather implement interleaving (or "interactive" behaviour) explicitely by:
interact f = do s <- getLine putStrLn (f s) interact f
(assuming line buffering) (Terminating with "ctrl-c")
Surely also something is needed for endless character resources as Tom pointed out.
Christian
In a lazy language, evaluation of arguments and results is interleaved. This coroutine-like behaviour is an important and pleasing characteristic, not a mistake to be avoided. Lazy evaluation of String -> String functions with the argument attached to an input device (eg. keyboard) and the result attached to an output device (eg. screen) is therefore a conceptually lean and natural way to express sinple interactive programs in a lazy functional language. Historically, the wish to preserve the option of looking at interaction this way, if only for pedagogical reasons, was the reason for keeping the interact function in Haskell even after the monadic revolution. If line-buffering is needed, it is easily programmed in Haskell as a (lazily evaluated!) function lineBuffered :: String -> String. If f :: String -> String is the core function of the program one can define main = interact (f . lineBuffered) and the fact that the program relies on line-buffered input is clearly expressed in the program itself. Conversely, if line-buffering is built-in as part of interact, there is no way to program it out when defining interact's argument. Let not the eager imperative tail wag the lazy functional dog! Colin R
Colin Runciman wrote:
Let not the eager imperative tail wag the lazy functional dog!
Ideally functional programs should be independent of evaluation strategy and I assume that this is the case for about 90% of all Haskell programs. This leaves maybe the head or only the nose for laziness of the "functional dog". But looking at the two actions of interact: interact f = do s <- getContents putStr (f s) I would expect the first action to be finished before the second, (and I would not call it "interact" anymore after this discussion). Therefore, the "primitives" (getContents, putStr) behave "incorrect" to my taste, (although the actual behaviour may be more desirable for special other purposes.) Christian
Christian Maeder wrote:
Colin Runciman wrote:
Let not the eager imperative tail wag the lazy functional dog!
Ideally functional programs should be independent of evaluation strategy and I assume that this is the case for about 90% of all Haskell programs. This leaves maybe the head or only the nose for laziness of the "functional dog".
"Ideally"? You just proved that you never *needed* laziness in your life. There is a full-fledged category of functional programs which wouldn't work without laziness. Saying that it is 10, or 0.1% has simply no sense. Colin demonstrated one such category. I need laziness to implement co-recursive data structures for scientific applications. (If you wish, have another Great Truth: "Ideally any programs should be independent of the language used for coding them..." Now, try to convince the world. ) Jerzy Karczmarczuk
But looking at the two actions of interact:
interact f = do s <- getContents putStr (f s)
(The Haskell report has two more actions, btw, setting nobuffering here)
I would expect the first action to be finished before the second, (and I
Why? The magic here, in any case, is in getContents, which returns a list that is *lazily evaluated as needed* (Haskell report, page 98 (sec 7.1)). hGetContents does the same for an arbitrary handle. This allows you to replicate the behaviour of Unix cat, ncat, grep etc, without having to code it explicitly. For the use of laziness, consider let fib = 0 : 1 : zipWith (+) fib (tail fib) in fib and think what would happen if "let" was strict. Programming in Haskell can be much more convenient than in strict languages, and laziness is assumed in lots of little ways throughout idiomatic Haskell code (I'm thinking of the liberal use of "where" and "let" bindings, for example).
would not call it "interact" anymore after this discussion).
Therefore, the "primitives" (getContents, putStr) behave "incorrect" to my taste, (although the actual behaviour may be more desirable for special other purposes.)
getContents behaves according to the specification in the standard, which is good enough for me. So does putStr.
Christian
HTH. --KW 8-)
I wrote:
But looking at the two actions of interact:
interact f = do s <- getContents putStr (f s)
I would expect the first action to be finished before the second
Keith Wansbrough wrote:
Why?
Because the actions are written down in that order? Why not? Why should I expect pipelining? Occam's razor demands the simplest theory.
For the use of laziness, consider
let fib = 0 : 1 : zipWith (+) fib (tail fib) in fib
and think what would happen if "let" was strict. Programming in Haskell can be much more convenient than in strict languages, and laziness is assumed in lots of little ways throughout idiomatic Haskell code (I'm thinking of the liberal use of "where" and "let" bindings, for example).
I know and appreciate these uses of laziness. There are plenty of examples where eager or lazy evaluation can bite you or save you. It's good to have both, and even better if you do not need to worry about it too often.
getContents behaves according to the specification in the standard, which is good enough for me. So does putStr.
Allow me to have another opinion, if the consequence is interleaved in- and output (when I don't want it). Can actually someone supply an implementation of something like interact that does no pipelining for the argument "id"? Simply doing "putStr !$ f !$ s" was not enough!
HTH.
Thanks, Christian
On Wed, Oct 01, 2003 at 04:42:51PM +0200, Christian Maeder wrote:
Can actually someone supply an implementation of something like interact that does no pipelining for the argument "id"? Simply doing "putStr !$ f !$ s" was not enough!
The simplest working but not necessarily correct solution could be: interact f = do s <- getContents length s `seq` putStr (f s) This one is probably better: interact f = do s <- getContents foldr seq () s `seq` putStr (f s) And this is more fun ;) : interact f = do s <- getContents foldr seq (putStr (f s)) s
Thanks, Christian
Best regards, Tom -- .signature: Too many levels of symbolic links
Allow me to have another opinion, if the consequence is interleaved in- and output (when I don't want it).
Can actually someone supply an implementation of something like interact that does no pipelining for the argument "id"? Simply doing "putStr !$ f !$ s" was not enough!
Yes, of course. Your code above only forces the evaluation of the first cons-cell of the list, which is not enough. You want to force the entire list. Try deepSeq :: [a] -> b -> b deepSeq (x:xs) y = deepSeq xs y deepSeq [] y = y noninteract f = do s <- getContents putStr (f (deepSeq s s)) or if you want non-lazy output too, reallynoninteract f = do s <- get Contents let r = f (deepSeq s s) putStr (deepSeq r r) <warn>untested code!</warn> There's a library containing such functions, called (IIRC) DeepSeq or something similar. HTH. --KW 8-)
On Wed, 1 Oct 2003, Keith Wansbrough wrote:
Can actually someone supply an implementation of something like interact that does no pipelining for the argument "id"? Simply doing "putStr !$ f !$ s" was not enough!
Yes, of course.
Your code above only forces the evaluation of the first cons-cell of the list, which is not enough. You want to force the entire list. Try
deepSeq :: [a] -> b -> b deepSeq (x:xs) y = deepSeq xs y deepSeq [] y = y
noninteract f = do s <- getContents putStr (f (deepSeq s s))
Here's another way to write the above: import DeepSeq noninteract f = getContents >>= (putStr . f $!!) -- Dean
Can actually someone supply an implementation of something like interact that does no pipelining for the argument "id"? Simply doing "putStr !$ f !$ s" was not enough!
Yes, of course.
Your code above only forces the evaluation of the first cons-cell of the list, which is not enough. You want to force the entire list.
Right, thanks to everybody who provided solutions! Christian
Christian Maeder wrote:
Malcolm Wallace wrote: [...]
Surely the name suggests that "interactive" behaviour is required, i.e. exactly some interleaving of input and output. The chunk-size of the interleaving should depend only on the strictness of the argument to "interact".
I'm not happy that interleaving depends on the strictness. Lazy or strict evaluation should only change the behaviour of overall termination (lazy evaluation should terminate more often).
I disagree with your point of view. Non-strictness is an essential feature of Haskell that any Haskell programmer should learn about soon. The use of an interleaving function interact helps to understand non-strictness. Also it shows that Haskell doesn't need a set of additinal primitives to deal with IO (the IO monad), but that non-strictness can provide the basis for IO. You only need as single primitive function, interact, that connects your non-strict IO function to the external world. I do not claim that this IO model is the best for programming in the large, but you can learn a lot from it.
Surely also something is needed for endless character resources as Tom pointed out.
An "interactive" interact is fine for that. Olaf -- OLAF CHITIL, Dept. of Computer Science, The University of York, York YO10 5DD, UK. URL: http://www.cs.york.ac.uk/~olaf/ Tel: +44 1904 434756; Fax: +44 1904 432767
I wrote:
"main=interact id" basically echoes every line of my input, whereas "main=interact show" correctly waits for EOF before outputting something.
The unix "cat" and "sort" behave in a similar way ("sort" obviuously has to wait for the last line.) Still I would regard it to be more "pure" (or "abstract") if my input would always be mapped to visibly separate output. Christian
participants (9)
-
Christian Maeder -
Colin Runciman -
Dean Herington -
Jerzy Karczmarczuk -
Keith Wansbrough -
Malcolm Wallace -
Olaf Chitil -
Simon Marlow -
Tomasz Zielonka