Chapter 6 Exceptions
“Ever tried. Ever failed. No Matter. Try again. Fail again. Fail better.”
OCaml as any other flavor of the ML-language family is a safe language. This is ensured by static type checking and by dynamic checks that rule out violations that cannot be detected statically. Examples of run time exceptions are:
1 2 3 4 5 6 7 8 9
Copyright By PowCoder代写 加微信 powcoder
Exception: Division_by_zero.
Here the expression 3 / 0 will type check, but evaluation will incur a runtime fault that is signalled by rasing the exception Division_by_zero. This is an example, where the expression 3 / 0 has a type, namely int, it has not value, but it does have an effect, namely it raises the exception Division_by_zero.
Another example of a runtime exception is Match_failure.
# let head Characters let head
Warning 8: Here is an []
val head : # head [];; Exception: #
(x::t) = x ;;
(x::t) = x ;;
^^^^^^^^^^
this pattern-matching is not exhaustive. example of a value that is not matched:
’a list -> ’a =
Match_failure (“//toplevel//”, 37, -60).
The function definition of head and the call head [] type check. However, head [] does not have a value, but has an effect.
67 c B. Pientka – Fall 2020
Chapter 6: Exceptions 6.1 It’s all about control
So far, we have considered built-in exceptions. Since they are pre-defined, they have a built-in meaning. However, we can also introduce and define our own excep- tions to signal a specific program error.
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15
exception Domain
let fact n = let rec f n =
if n = 0 then 1
else n * f (n-1) in
if n < 0 then raise Domain else f(n)
let runFact n = try
let r = fact n in
print_string ("Factorial of " ^ string_of_int n ^ " is " ^
string_of_int r ^ "\n")
with Domain -> print_string “Error: Invariant violated — trying to call
factorial on inputs < 0 \n"
6.1 It’s all about control
One of the most interesting uses of exceptions is for backtracking or more generally controlling the execution behaviour. Backtracking can be implemented by looking greedily for a solution, and if we get stuck, we undo the most recent greedy decision and try again to find a solution from that point. There are many examples where solutions can be effectively found using backtracking. A classic example is searching for an element in a tree.
Specifically, we want to implement a function find t k where t is a binary tree and k is a key. The function find has the type (’a * ’b) tree -> ’a -> ’b, i.e. we store pairs of keys and data in the tree. Given the key k (the first component) we return the cor- responding data entry d, if the pair (k,d) exists in the tree. We make no assumptions about the tree being a binary search tree. Hence, when we try to find a data entry, we may need to traverse the whole tree. We proceed as follows.
• If the tree is Empty, then we did not find a data corresponding to the key k. Hence we fail and raise the exception NotFound.
68 c B. Pientka – Fall 2020
Chapter 6: Exceptions
6.1 It’s all about control
exception NotFound
type key = int
type ’a tree = | Empty
| Node of ’a tree * (key * ’a) * ’a tree
letrecfind tk=matchtwith | Empty -> raise NotFound
| Node (l, (k’,d), r) ->
if k = k’ then d
try find l k with NotFound -> find r k
1 2 3 4 5 6 7 8 9
10 11 12 13 14
To evaluate find t 55 we proceed as follows:
1 find t 55
2 !⇤ try find l 55 with NotFound -> find r 55
Note that we install on the call stack an exception handler to which we return in the event of find l 55 returning the exception NotFound; in this case, we then proceed to compute find r 55.
However first, we must evaluate find l 55.
1 find l 55
2 !⇤ try find ll 55 with NotFound -> find lr 55
3 !⇤ try (try find Empty 55 with NotFound -> find Empty 55)
4 with NotFound -> find lr 55
5 !⇤ try (try raise NotFound with NotFound -> find Empty 55)
6 with NotFound -> find lr 55
7 !⇤ try find Empty 55
8 with NotFound -> find lr 55
9 !⇤ try (raise NotFound)
10 with NotFound -> find lr 55
11 !⇤ find lr 55
12 !⇤ try find Empty 55 with NotFound -> find Empty 55
13 !⇤ try (raise NotFound) with NotFound -> find Empty 55
14 !⇤ find Empty 55
15 !⇤ raise NotFound
Hence another exception handler is installed in line 2. If the evaluation of find ll 55 returns an exception NotFound, we proceed with computing find lr 55. Line 3
69 c B. Pientka – Fall 2020
To understand what happens, let’s see how we evaluate find t 55 in a tree t which is defined as follows:
let ll = Node (Empty, (3, “3”), Empty)
let lr = Node (Empty, (44, “44”), Empty)
let l = Node ( ll, (7, “7”), lr)
let r = Node ( Node (Empty, (55, “55”), Empty) , (7, “7”), Empty) let t = Node ( l, (1, “1”), r)
Chapter 6: Exceptions 6.1 It’s all about control
shows the evaluation of find lr 55. Note that another excpetion handler is installed. Now, find Empty 55 raises the exception NotFound (line4). It is handled by the innermost handler (see line 6) and we proceed to evaluate find Empty 55. Note here Empty denotes the right subtree of ll. This again triggers the exception NotFound and we proceed to find lr 55. This will eventually also raise the exception NotFound.
As a consequence, we return to the first exception handler that was installed when we were starting the evaluation:
1 find t 55
2 !⇤ try find l 55 with NotFound -> find r 55
eventually steps to
At this point we have found our answer ’’55’’ and we simply return it.
7 !⇤ “55”
try (raise NotFound) with NotFound -> find r 55 findr55
try find (Node (Empty, (55, “55”), Empty)) 55
with NotFound -> find Empty 55 try”55″
with NotFound -> find Empty 55
As tracing through the evaluation shows in each recursive call, we install another exception handler. If a recursive call raises an excpetion, then it is handled by the innermost handler and propagated up.
Exception handling provides a way of transferring control and information from some point in the execution of a program to a handler associated with a point previ- ously passed by the execution (in other words, exception handling transfers control up the call stack).
We want to emphasize that using exceptions to transfer controll is not unique to OCaml nor is it unique to functional langauages. It exists in many languages such as C++, Java, JavaScript, etc. and is always a way to transfer controll.
70 c B. Pientka – Fall 2020
程序代写 CS代考 加微信: powcoder QQ: 1823890830 Email: powcoder@163.com