150 likes | 290 Views
Standard ML. Exceptions. Exceptions – The Need. An extensive part of the code is error handling A function can return an answer, or fail to find one, or signal that a solution does not exist. We can try to use ML datatypes:’ datatype int sol = Success of int | Failure | Impossible
E N D
Standard ML Exceptions
Exceptions – The Need • An extensive part of the code is error handling • A function can return an answer, or fail to find one, or signal that a solution does not exist. • We can try to use ML datatypes:’ datatype int sol = Success of int | Failure | Impossible • Using special return values can be tedious, and requires explicit handling every time casemethodA problem of Success s => Int.toString s | Failure => (casemethodB problem of Success s => Int.toString s | Failure => "Both failed" | Impossible => "No Good") | Impossible => "No Good“ • Sometimes we don’t really know what to do with the error, so we we’ll return a sol…
Using Exceptions • Separates the exceptional path from the normal path • Upon error, we raise an exception funhd [] = raise Empty | hd (x::xs) = x • A raised exception is called packet • The packet propagates … fun add_hd xsys = (hdxs) + (hdys) • … up to the first point where a handlerfor it exists val x = add_hd [1] [] handle Empty => 1 • The caller of a function doesn’t have to check any of the error values • The whole expression is a single expression
The Exception type exn • We can raise only a specific type: the built-in type exn • The type exn is a special datatype with an extendable set of constructors and values • Declaring exceptions - values of type exn - exceptionFailure; - Failure; val it = Failure(-) : exn • Declaring exceptions – Constructors: - exceptionProblemof int; - Problem; val it = fn : int -> exn
The Exception type exn • In normal expressions, exn behaves pretty much like a regular datatype (though it is not an equality type) • Values of type exn have all the privileges of values of other types - val x = Failure; (* Failure is a value *) val x = Failure(-) : exn - val p = Problem 1; (* Problem is a constructor *) val p = Problem(-) : exn - map Problem [0, 1, 2]; val it = [Problem(-),Problem(-),Problem(-)] : exn list - fun what's_the_problem (Problem p) = p; Warning: match nonexhaustive Problem x => ... val what's_the_problem = fn : exn -> int
Raising Exceptions - Semantics • raise Exp • The expression Exp of type exn is evaluated to e • raise Exp evaluates to an exception packetcontaining e • Packets are not ML values • Packets propagates under the call-by-value rule • Which means that all of the following evaluate toraise Exp • f (raise Exp) • (raise Exp) arg • raise (Exp1 (raise Exp)) (* Exp1 is constructor *) • (raise Exp, raise Exp1) (* or {a=Exp, b=Exp1} *) • letval name = raise Exp insome_expressionend • localval name = raise Exp insome_declarationend
The Computation of take fun take (0, _) = [] | take (n, x::xs) = x :: take (n-1, xs) | take (n, nil) = raise Empty • fun reverse3 xs = rev (take (3, xs)) • reverse3 [1,2] • rev (take (3, [1, 2])) • rev (1::take (2, [2])) • rev (1::(2::take (1, []))) • rev (1::(2::raise Empty)) • rev (1::raise Empty) • rev (raise Empty) • raise Empty
Handling Exceptions - Syntax • General form: Exp_0handleP1 => Exp_1| . . .|Pn => Exp_n • Assuming Pis are disjoint, this is equivalent to (((Exp_0handleP1 => Exp_1) … ) handle Pn => Exp_n) • All Exp_i must be type-compatible • All Pi must be valid pattern for the type exn • Calculating length using exceptions - fun len l = 1 + len (tl l) handle Empty => 0;
Handling Exceptions - Semantics Exp_0handleCons1 x => Exp_1 • Assume Exp_0 evaluates to some value V Then the value of this expression is: • Exp1 If Exp_0 evaluate to raise Cons1 x • V otherwise. V may be either a normal value, or a non-matching raised exception. • Handle is a short-circuit operator, like if-then-else • All this is exactly equivalent to the familiar notions from C++
The Computation of len • funtl [] = raiseTl | tl (x::xs) = xs • fun len xs = 1 + (len (tlxs)) handle Tl => 0; • len [3] • 1+(len (tl [3])) handle Tl=>0 • 1+(len []) handle Tl=>0 • 1+(1+(len (tl [])) handle Tl=>0) handle Tl=>0 • 1+(1+(len (raise Tl)) handle Tl=>0) handle Tl=>0 • 1+(1+(raise Tl) handle Tl=>0) handle Tl=>0 • 1+(raise Tl handle Tl=>0) handle Tl=>0 • 1+0 handle Tl=>0 • 1 handle Tl=>0 • 1
The Type of raise Exp • The expression raise Exp is of type ‘a • Technically described as “t for some arbitrary type t” • It is NOT an expression of type exn! • Within context, it simply puts no restriction on other parts of the expression: - fun throw _ = raise Empty; val throw = fn : 'a -> 'b - fun bar x = if x>0 then x elseraise Underflow; val bar = fn : int -> int • Theoretically, It may be type-checked as type None (aka Bot) or something similar to datatype ‘a Ex = ‘a
Using Exception Handling - More • Example: If methodA fails then methodB is tried casemethodA problem of Success s => Int.toString s | Failure => (casemethodB problem of Success s => Int.toString s | Failure => "Both failed" | Impossible => "No Good") | Impossible => "No Good" • Exceptions give a shorter and clearer program. Error propagation does not clutter the code. toString (methodA problem handle Failure => methodB problem)handle Failure => "Both failed“ | Impossible => "No Good"
Error Messages - raise Problem; Error: argument of raise is not an exception [tycon mismatch] raised: int -> exn in expression: raise Problem • hd [“good”] handle nil => “bad”; Error: handler domain is not exn [tycon mismatch] handler domain: 'Z list in expression: hd ("good" :: nil) handle nil => "bad" | exn => raise exn
Exam Questions • What is the response • local exception E of string; in fun f (E “Hello”, E x) = x; end letexceptionExyof int in (fn 2 => raise Exy 4 | x => x+x) 2 handle Exy n => n end
Standard Exceptions Some built-in exceptions • Chr is raised by (chr k) if k<0 or k>255 • Match is raised for failure of pattern-matching. e.g. when an argument matches none of the function’s patterns, if a case expression has no pattern that matches, etc. • Bind is raised if the value of E does not match pattern P in the declaration val P = E