There are 3 important functions when dealing with interactive Haskell programs:
interact takes a function of type
String -> String, and the entire input from stdin is passed to this function as its input, and the resulting string is output on stdout. Because of the nature of Haskell, the output will seem interactive as it gets evaluated bit by bit; but the function provided is written as if the whole input is specified at once. Remember to think of functional programming as not having a concept of time.
unlines takes a list, and returns it interleaved with newlines, such as:
Input: unlines ["aa","bb","cc","dd","ee"] Output: "aa\nbb\ncc\ndd\nee\n"
lines is the reverse of unlines, essentially:
Input: lines "aa\nbb\nbb" Output: ["aa","bb","bb"]
Writing interactive programs
Generally, interactive programs seem to be of the form:
some_fun :: Show argument => [argument] -> String -> String some_fun xs = unlines . map doSomething . lines
Though there are more complicated variations (the 2009 paper uses a mapState function which is a state machine).
Why does this work?
Haskell uses ‘Lazy Evaluation’ which is a strategy for program reduction. It works by not evaluating arguments to functions until they are needed, and even then only evaluating as much as is needed by the function. This is why infinite data structures can be used in Haskell. The following would be impossible with strict evaluation…
numbers = numsFrom 0 where numsFrom n = n : numsFrom (n+1) head numbers
…because the programming language would try to evaluate the entire
numbers list (which is infinite) just to return the first element. In Haskell, due to lazy evaluation, this will work.
An interactive program can therefore be formulated as a lazily evaluated function from a list of the user’s inputs to a list of program’s outputs. The computation of the program’s output list always proceeds as far as possible when evaluating the user’s input list, suspending reduction only when the next item in the list is strictly needed.