• No results found

9.3 Other uses of exceptions

9.3.4 The exn type

We close with a somewhat unorthodox use of exceptions completely unrelated to con- trol flow. Exceptions (values of theexntype) are first-class values; they can be passed as arguments, stored in data structures,etc. The values in theexntype are specified withexceptiondefinitions. One unique property of theexntype is that it isopenso that new exceptions can be declared when desired. This mechanism can be used to provide a kind of dynamic typing, somewhat like the polymorphic variants discussed in Section 6.5.

For example, suppose we want to define a list of values, where the type of the values can be extended as desired. Initially, we might want lists containing strings and integers. Suppose we wish to define a functionsuccthat increments every integer in

the list, preserving all other values. # exceptionString of string;; # exceptionInt of int;; # letsucc l =

List.map (fun x -> match xwith

Int i -> Int (i + 1) | _ -> x) l;;

val succ : exn list -> exn list = <fun>

# letl = succ [String "hello"; Int 1; Int 7];;

val l : exn list = [String "hello"; Int 2; Int 8]

Later, we might also decide to add floating-point numbers to the list, with their own successor function.

# exceptionFloat of float;;

exception Float of float

# letsucc_float l = List.map (fun x ->

match xwith

Float y -> Float (y +. 1.0) | _ -> x) l;;

val succ_float : exn list -> exn list = <fun>

#succ_float (Float 2.3 :: l);;

- : exn list = [Float 3.3; String "hello"; Int 2; Int 8]

The main purpose of this example is to illustrate properties of exception values. In cases where extendable unions are needed, the use of polymorphic variants is more appropriate. Needless to say, it can be quite confusing to encounter data structures constructed from exceptions!

9.4. EXERCISES CHAPTER 9. EXCEPTIONS

9.4 Exercises

Exercise 9.1 Which of the following are legal expressions? 1. exception A

2. exception b

3. exception C of string

4. exception D of exn

5. exception E of exn let x = E (E (E Not_found))

6. let f () = exception F raise F

Exercise 9.2 What is the result of evaluating the following programs? 1. exception A

try raise Awith A -> 1 2. exception Aof int let f i = raise(A (100 / i));; let g i = try f iwith A j -> j;; g 100 3. exception Aof int let rec f i = if i = 0then raise(A i) else g (i - 1) and g i = try f iwith A i -> i + 1;; g 2

Exercise 9.3 In the following program, the functionsum_entriessums up the integer values associated with each name in the list names. The List.assocfunction finds the value associated with the name, raising theNot_foundexception if the entry is not found. For example, the expressionsum_entries 0 ["a"; "c"]would evaluate to35, and the expressionsum_entries 0 ["a"; "d"]would raise theNot_foundexception.

let table = [("a", 10); ("b", 20); ("c", 25)] let rec sum_entries total (names : string list) =

match nameswith name :: names’ ->

sum_entries (total + List.assoc name table) names’ | [] ->

CHAPTER 9. EXCEPTIONS 9.4. EXERCISES Suppose we wish to catch the exception, arbitrarily assigning a value of 0 to each unknown entry. What is the difference between the following two functions? Which form is preferable?

1. let table = [("a", 10); ("b", 20); ("c", 25)] let rec sum_entries total (names : string list) =

match nameswith name :: names’ ->

(try sum_entries (total + List.assoc name table) names’ with Not_found ->

sum_entries total names’) | [] ->

total

2. let table = [("a", 10); ("b", 20); ("c", 25)] let rec sum_entries total (names : string list) =

match nameswith name :: names’ ->

let i =

try List.assoc name table with Not_found ->

1 in

sum_entries (total + i) names’ | [] ->

total

Exercise 9.4 Suppose we are given atableas in the last exercise, and we wish to call

some function fon one of the entries, or returning 0 if the entry is not found. That

is, we are given the functionf, and a name, and we wish to evaluatef (List.assoc table name). What is the difference between the following functions?

1. let callf f name =

try f (List.assoc table name) with Not_found ->

0

2. let callf f name = let i =

try Some (List.assoc table name) with Not_found -> None in match iwith Some j -> f j | None -> 0

Exercise 9.5 The expressioninput_line stdinreads a line of text from standard in- put, returning the line as a string, or raising the exceptionEnd_of_fileif the end of the file has been reached. Write a functioninput_linesto read all the lines from the

9.4. EXERCISES CHAPTER 9. EXCEPTIONS channelstdin, returning a list of all the lines. The order of the lines in the list does not

Chapter 10

Input and Output

The I/O library in OCaml is fairly expressive, including a library (theUnixlibrary) that implements a set of system calls that are portable across the platforms on which OCaml runs. In this chapter, we’ll cover many of the standard built-in I/O functions.

The I/O library starts by defining two data types: the typein_channelspecifies

an I/O channel from which characters can be read, and the typeout_channelspecifies

an I/O channel to which characters can be written. I/O channels may represent files, communication channels, or some other device; the exact operation depends on the context.

At program startup, there are three channels open, corresponding to the standard file descriptors in Unix; stdinis the standard input stream, stdout is the standard output stream, andstderris the standard output stream for error messages.

val stdin : in_channel val stdout : out_channel val stderr : out_channel

10.1 File opening and closing

There are two functions to open an output file: the functionopen_outopens a file for

writing text data, and the functionopen_out_binopens a file for writing binary data.

These two functions are identical on a Unix system. On some Macintosh and Microsoft Windows systems, theopen_outfunction performs line termination translation, while

theopen_out_binfunction writes the data exactly as written. These functions raise the exceptionSys_errorif the file can’t be opened; otherwise they return anout_channel.

A file can be opened for reading with the functionsopen_inandopen_in_bin. val open_out : string -> out_channel

val open_out_bin : string -> out_channel val open_in : string -> in_channel val open_in_bin : string -> in_channel

10.2. WRITING AND READING VALUES ON A CHANNELCHAPTER 10. INPUT AND OUTPUT