home *** CD-ROM | disk | FTP | other *** search
-
-
- Introduction to Gofer 12. DIALOGUES: INPUT AND OUTPUT
-
-
- 12. DIALOGUES: INPUT AND OUTPUT
-
- The Gofer system implements a subset of the facilities for programs
- involving I/O described in the Haskell report [5]. In particular, this
- makes it possible for Gofer programs to be run interactively, and to
- make limited use of text files for both reading and writing. A
- significant factor in the design of the Haskell I/O facilities is that
- it allows the use of such programs without loss of referential
- transparency.
-
- 12.1 Basic description
- ----------------------
- Programs using the I/O facilities in Gofer are modelled by functions of
- type Dialogue, defined by the type synonym:
-
- type Dialogue = [Response] -> [Request]
-
- In other words, a Gofer program produces a list of output values, each
- of which may be thought of as a request for some particular input or
- output action, and obtains the corresponding list of operating system
- responses as its input. Note that the input list of responses will be
- evaluated lazily; i.e. we can ensure that we do not attempt to obtain
- the response to a given request until that request has been completed.
-
- The current range of requests supported by Gofer is described by the
- following datatype definition, taken from the standard prelude:
-
- data Request = -- file system requests:
- ReadFile String
- | WriteFile String String
- | AppendFile String String
- -- channel system requests:
- | ReadChan String
- | AppendChan String String
- -- environment requests:
- | Echo Bool
-
- Each response is an element of the type defined by the following
- datatype definition, using an auxiliary datatype IOError to describe a
- variety of error conditions that may occur:
-
- data Response = Success
- | Str String
- | Failure IOError
-
- data IOError = WriteError String
- | ReadError String
- | SearchError String
- | FormatError String
- | OtherError String
-
- The following list describes the kind of I/O behaviour specified by
- each form of Request and indicates the possible Response values that
- may be obtained in each case:
-
- o ReadFile string: Read contents of file named by "string".
-
-
- 49
-
-
-
-
- Introduction to Gofer 12.1 Basic description
-
-
- Possible responses to this request are:
-
- o Str contents if the request is successful, where "contents" is
- a string (evaluated lazily) containing the contents of the the
- file specified by the ReadFile request.
-
- o Failure (SearchError name) occurs if file "name" cannot be
- accessed.
-
- o Failure (ReadError name) occurs if some other error occurs
- whilst opening the file "name".
-
- o WriteFile name string: Write the given "string" to the file
- "name". If the file does not already exist, it is created before
- attempting to write the value to file. If the file already exists
- then it will be truncated to zero length before the write begins.
- No response is obtained until the string argument has been fully
- evaluated and its contents written to file. Possible responses
- are:
-
- o Success if the write to file was completed successfully.
-
- o Failure (WriteError msg) if an error was detected whilst
- trying to perform the output. If the problem occurred whilst
- attempting to open the specified file, then "msg" contains
- the filename, otherwise it contains a printable
- representation of the evaluation error which occurred.
-
- o AppendFile name string: Similar to the WriteFile request except
- that the value of the given "string" is appended onto the file
- "name" if that file already exists. The responses that may be
- obtained from this request are the same as those for WriteFile.
-
- o ReadChan name: Read from the input stream "name". Note that
- it is an error to attempt to read from the same channel more than
- once in the same program. Possible responses are:
-
- o Str contents if the request is successful, where "contents"
- is a string (evaluated lazily) containing the list of
- characters entered on the input stream.
-
- o Failure (SearchError name) if the named channel cannot be
- found. The only input channel known to Gofer is the standard
- input channel "stdin". For convenience, the standard prelude
- defines the variable stdin bound to this string.
-
- o Failure (ReadError name) if a ReadChan request for the named
- channel has already been given by a previous request.
-
- o AppendChan name string: Output "string" on channel "name". No
- response is obtained until the string has been fully evaluated and
- written to the named channel. Possible responses are:
-
- o Success if the append to channel was completed successfully.
-
- o Failure (SearchError name) if the named channel cannot be
-
-
- 50
-
-
-
-
- Introduction to Gofer 12.1 Basic description
-
-
- found. The only output channels known to Gofer are "stdout",
- "stderr" and "stdecho" (which is actually just another name
- for "stdout" in Gofer). For convenience, the standard
- prelude defines variables stdout, stderr and stdecho bound to
- the corresponding string values.
-
- o Failure (WriteError msg) if an error is detected whilst
- trying to perform the output. The string "msg" contains a
- printable representation of the evaluation error which
- occurred.
-
- o Echo status: Set the echo status on the standard input channel
- stdin to the given boolean value. If the echo status is True,
- then user input will be echoed onto the screen as they are typed
- and the usual line editing facilities (such a backspace or delete)
- provided by the host system can be used to edit the input lines as
- they are entered. If the echo status is False, then individual
- characters may be read from the standard input channel without any
- echo or line editing features.
-
- Note that at most one Echo request can be used in a program, and
- must precede any ReadChan request for stdin. If not set by an
- explicit Echo request, the echo status defaults to True. Possible
- responses are:
-
- o Success if the request was completed successfully.
-
- o Failure (OtherError msg) if the request could not be
- completed either because a readChannel request for stdin has
- already been processed, or because a previous Echo request
- has already been given. The corresponding values of "msg"
- are "stdin already in use" and "repeated Echo request"
- respectively.
-
- A simple example of a program using these facilities to output a short
- message on the standard output stream is:
-
- helloWorld :: Dialogue
- helloWorld resps = [AppendChan stdout "hello, world"]
-
- Any expression entered into Gofer of type "Dialogue" will be treated as
- a Gofer program using I/O and will be executed accordingly:
-
- ? helloWorld
- hello, world
- (1 reduction, 28 cells)
- ?
-
- Notice that without the explicit type declaration, the type that would
- be inferred for for helloWorld would be a -> [Request], and hence
- helloWorld would not be executed as a Dialogue program. This point can
- be illustrated using lambda expressions:
-
- ? \resps -> [AppendChan stdout "hello, world"]
- v128
- (1 reduction, 7 cells)
-
-
- 51
-
-
-
-
- Introduction to Gofer 12.1 Basic description
-
-
- ? (\resps -> [AppendChan stdout "hello, world"]) :: Dialogue
- hello, world
-
- (1 reduction, 28 cells)
- ?
-
- In many cases the structure of an expression is enough to fully
- determine its type as Dialogue (or equivalently as [Response] ->
- [Request]), in which case no explicit types are required to ensure that
- the expression is treated as a Gofer program using I/O:
-
- ? \~[Success] -> [AppendChan stdout "hello, world"]
- hello, world
- (1 reduction, 29 cells)
- ?
-
- Note the use of the irrefutable pattern ~[Success] for the lambda
- expression in the last example; without this, the usual rules of
- pattern matching as described in section 9 would force Gofer to try and
- match the pattern [Success] against the list of responses, before the
- corresponding request had been produced:
-
- ? \ [Success] -> [AppendChan stdout "hello, world"]
-
- Aborting Dialogue:
- {error "Attempt to read response before request complete"}
- (50 reductions, 229 cells)
- ?
-
- The next example takes a single string a parameter and displays the
- contents of the corresponding file:
-
- showFile :: String -> Dialogue
- showFile name ~(read:_) = [ReadFile name, AppendChan stdout result]
- where result = case read of Str contents -> contents
- Failure _ -> "Can't open " ++ name
-
- With a few modifications, we can implement a similar program which
- prompts for, and reads, a filename from the standard input and then
- reads and displays the contents of that file as before. This program
- is based on a similar example in the Haskell report [5]:
-
- main ~(Success : ~(Str userInput : ~(r3 : _)))
- = [ AppendChan stdout "Please type a filename: ",
- ReadChan stdin,
- ReadFile name,
- AppendChan stdout (case r3 of Str contents -> contents
- Failure _ -> "Can't open "
- ++ name)
- ] where (name : _) = lines userInput
-
-
-
-
-
-
-
-
- 52
-
-
-
-
- Introduction to Gofer 12.2 Continuation style I/O
-
-
- 12.2 Continuation style I/O
- ---------------------------
- As an alternative to the `stream-based' approach to programs using the
- I/O facilities in Gofer, the standard prelude defines a family of
- functions which enables such programs to be written in a `continuation'
- style. The basic idea is to define a function corresponding to each
- different kind of request, whose parameters include the values required
- to make the request together with two continuations. The continuations
- are functions describing "what to do next", one of which is used if the
- request is successful, the other if the request fails.
-
- As an example, the ReadFile request is represented by the function
- "readFile" whose definition is equivalent to:
-
- readFile name fail succ ~(r:rs) = ReadFile name : rest rs
- where rest = case r of Str s -> succ s
- Failure ioerror -> fail ioerror
-
- The first thing to happen when a dialogue expression of the form
- "readFile name fail succ" is evaluated is that the corresponding
- request "ReadFile name" is added to the list of I/O requests. A new
- dialogue value "rest" is chosen, depending on the response to the
- ReadFile request, and the program continues by passing the remaining
- part of the response list to "rest". The functions "succ" and "fail"
- (called the success and failure continuations respectively) describe
- the way in which the new dialogue "rest" is obtained.
-
- The following example (edited a little to fit within the margins of this
- document) shows how the readFile function described above can be used to
- print the contents of a file called "test" on the display:
-
- ? readFile "test" (\ioerror resps -> [])
- (\s resps->[AppendChan stdout s])
- This is a test message
-
- (4 reductions, 52 cells)
- ?
-
- The success continuation "(\s resps->[AppendChan stdout s])" used here
- receives the contents of the file "test" in the the parameter "s" and
- uses an AppendChan request to output that string on the display. As
- this example shows, the stream based approach of the previous section
- can be combined with the continuation based style of I/O without any
- difficulty. The failure continuation "(\ioerror resps -> [])" ignores
- the error condition "ioerror" which caused the request to fail and
- gives a dialogue which terminates immediately without any action. For
- example, assuming that the file "Test" cannot be found:
-
- ? readFile "Test" (\ioerror resps -> [])
- (\s resps->[AppendChan stdout s])
-
- (4 reductions, 24 cells)
- ?
-
- In practice, it is usually a good idea to produce some kind of
- diagnostic message when an error occurs:
-
-
- 53
-
-
-
-
- Introduction to Gofer 12.2 Continuation style I/O
-
-
- ? readFile "Test"
- (\ioerror resps -> [AppendChan stdout (show' ioerror)])
- (\s resps -> [AppendChan stdout s])
- SearchError "Test"
- (11 reductions, 59 cells)
- ?
-
- In each of the examples above, the failure continuation has type
- "FailCont" as defined by the following type synonym in the standard
- prelude:
-
- type FailCont = IOError -> Dialogue
-
- Similarly, the success continuation, which takes a string representing
- an input string and produces a new Dialogue has type "StrCont":
-
- type StrCont = String -> Dialogue
-
- A third kind of continuation is needed for those requests which return
- a response of the form "Success" if successful (e.g. output
- requests). In this case the continuation is simply another dialogue:
-
- type SuccCont = Dialogue
-
- The following list gives the type of each of the six functions
- corresponding to the six different kinds of I/O request described in
- the previous section. Full definitions for each of these functions are
- given in appendix B:
-
- readFile :: String -> FailCont -> StrCont -> Dialogue
- writeFile :: String -> String -> FailCont -> SuccCont -> Dialogue
- appendFile :: String -> String -> FailCont -> SuccCont -> Dialogue
- readChan :: String -> FailCont -> StrCont -> Dialogue
- appendChan :: String -> String -> FailCont -> SuccCont -> Dialogue
- echo :: Bool -> FailCont -> SuccCont -> Dialogue
-
- As an illustration of the use of these functions, we show how each of
- the example programs from the previous section can be rewritten using
- the continuation based style of I/O, starting with the program
- "helloWorld":
-
- helloWorld :: Dialogue
- helloWorld = appendChan stdout "hello, world" abort done
-
- In this case, the explicit type declaration is not actually required
- since the type of the expression is completely determined by the type
- of "appendChan". The failure continuation "abort" is equivalent to the
- function "(\ioerror resps -> [])" described above and terminates the
- program if an error occurs without any further action. In a similar
- way, "done" is the trivial dialogue which terminates immediately
- without any action. Both of these values are defined in the standard
- prelude:
-
- done :: Dialogue
- done resps = []
-
-
-
- 54
-
-
-
-
- Introduction to Gofer 12.2 Continuation style I/O
-
-
- abort :: FailCont
- abort ioerror = done
-
- Using the same approach, the "showFile" and "main" programs from the
- previous section are written as:
-
- showFile :: String -> Dialogue
- showFile name
- = readFile name (\ioerror -> appendChan stdout
- ("Can't open " ++ name) abort done)
- (\contents-> appendChan stdout contents abort done)
-
- main :: Dialogue
- main = appendChan stdout "Please type a filename: " abort
- (readChan stdin abort
- (\userInput -> let (name : _) = lines userInput in
- readFile name
- (\ioerror -> appendChan stdout ("Can't open " ++ name)
- abort done)
- (\contents -> appendChan stdout contents abort done)))
-
-
- 12.3 Interactive programs
- -------------------------
- One of the principal motivations for including facilities for I/O in
- Gofer programs was to provide a way of using interactive programs as
- described in [1]. An interactive program is represented by a function
- of type String -> String mapping an input string of characters entered
- at the keyboard into an output string to be displayed on the screen.
-
- There are two functions defined in the standard prelude which can be
- used to `execute' functions of this kind as interactive programs:
-
- o "interact f" executes f::String->String as an interactive program
- with echo on. This means that characters are read from the
- keyboard a line at a time. The usual editing characters such as
- backspace can be used to correct mistakes which are noticed before
- the return key is pressed at the end of each line. The input
- stream can be terminated by typing an end of file character at the
- beginning of a line:
-
- ? interact (map toUpper)
- This text was entered using the interact function
- THIS TEXT WAS ENTERED USING THE INTERACT FUNCTION
- ^Z
- (874 reductions, 1037 cells)
- ?
-
- o "run f" behaves like "interact f" except that echo is turned off.
- In this case, the only way of terminating the input stream without
- reaching the end of the string produced by "f" is to use the
- interrupt key:
-
- ? run (map toUpper)
- ALTHOUGH THIS IS ENTERED IN LOWER CASE, IT STILL
- APPEARS IN UPPER CASE !
-
-
- 55
-
-
-
-
- Introduction to Gofer 12.3 Interactive programs
-
-
- {Interrupted!}
-
- (1227 reductions, 1463 cells)
- ?
-
- [ASIDE: of these two functions, only "interact" is also included in the
- standard prelude for Haskell, although "run" may also be added to a
- Haskell system using the definition below.]
-
- The definitions of "interact" and "run" provide further examples of
- Gofer programs using simple I/O facilities:
-
- interact :: (String -> String) -> Dialogue
- interact f = readChan stdin abort
- (\s -> appendChan stdout (f s) abort done)
-
- run :: (String -> String) -> Dialogue
- run f = echo False abort (interact f)
-
- [EXERCISE for the interested reader: construct alternative definitions
- for these functions using the stream based approach from section 12.1.]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 56
-
-
-