14 Fudgets for non-GUI I/O

14.1 Standard I/O fudgets

To read the standard input (usually the keyboard) and write to the standard output or standard error stream (the screen), you can use the fudgets:

stdinF   :: F a String
stdoutF  :: F String a
stderrF  :: F String a
The output from stdinF is the characters received from the program's standard input channel. For efficiency reasons, you do not get one character at a time, but larger chunks of characters. If you want the input as a stream of lines, you can use

inputLinesSP :: SP String String
which puts together the chunks and splits them at the newlines.

A simple example is a fudget that copies text from the keyboard to the screen with all letters converted to upper case:

stdoutF >==< (map toUpper >^=< stdinF)
It applies toUpper to all characters in the strings output by stdinF and then feeds the result to stdoutF.

Here is a fudget that reverses lines:

(stdoutF>=^<((++"\n").reverse))>==<(inputLinesSP>^^=<stdinF)
The precedences and associativities of the combinators are such that these fudgets can be written as:

stdoutF >==< map toUpper >^=< stdinF
stdoutF >=^< (++"\n").reverse >==< inputLinesSP >^^=< stdinF

14.2 Accessing the file system

The following fudgets allow you to read files, write files and get directory contents:

readFileF  :: F FilePath           (FilePath,Either IOError String)
writeFileF :: F (FilePath,String)  (FilePath,Either IOError ())
readDirF   :: F FilePath           (FilePath,Either IOError [FilePath])
These can be seen as servers, with a one-to-one correspondence between requests and responses. For convenience, the responses are paired with the file path from the request. The responses contain either an error message or the result of the request. The result is the contents of a file (readFile), a directory listing (readDirF), or () ( writeFileF).

14.3 The timer fudget

The timer fudget generates output after a certain delay and/or at regular time intervals. Its type is

data Tick = Tick
timerF :: F (Maybe (Int, Int)) Tick
The timer is initially idle. When it receives Just (i,d) on its input, it begins ticking. The first tick will be output after d milliseconds. Then, ticks will appear regularly at i millisecond intervals, unless i is 0, in which case only one tick will be output. Sending Nothing to the timer resets it to the idle state.

As a simple example, here is a fudget that outputs,once a second, the number of seconds that have elapsed since it was activated:

countSP >^^=< timerF >=^^< putSP (Just (1000,1000)) nullSP
  where countSP = mapAccumlSP inc 0
        inc n Tick = (n+1,n+1)