|
|
1.1 ! root 1: \chapter{Exceptions} ! 2: \label{exception} ! 3: The rough and ready rule for understanding how exceptions are handled ! 4: is as follows. If an exception is raised by a \verb"raise" ! 5: expression ! 6: \begin{quote} ! 7: \verb"raise" $E$ \verb"(" {\it exp} \verb")" ! 8: \end{quote} ! 9: which lies in the textual scope of a declaration of the exception ! 10: constructor $E$, then it may be handled by a handling rule ! 11: \begin{quote} ! 12: \verb"handle" $E$ \verb"(" {\it pat} \verb") =>" {\it exp'} ! 13: \end{quote} ! 14: but only if this handler is in the textual scope of the same ! 15: declaration. ! 16: ! 17: Any exception, regardless of scope, is handled by a wildcard or ! 18: variable pattern, as ! 19: \begin{quote} ! 20: \verb"handle _ =>" {\it exp'} ! 21: \end{quote} ! 22: This rule is perfectly adequate for exceptions declared at top level; ! 23: some examples in Section~\ref{pathexn} below illustrate what may occur in other ! 24: cases. ! 25: ! 26: \section{An example} ! 27: To illustrate the generality of exception handling, suppose we have ! 28: declared some exceptions as follows: ! 29: \begin{verbatim} ! 30: exception Oddlist of int and Oddstring of string ! 31: \end{verbatim} ! 32: and that a certain expression exp:int may raise either of these ! 33: exceptions and also runs the risk of dividing by zero. The handler ! 34: in the following \verb"handle" expression would deal with these ! 35: expressions: ! 36: \begin{verbatim} ! 37: exp handle Oddlist [] => 0 ! 38: | Oddlist [x] => 2*x ! 39: | Oddlist (x::y::_) => x div y ! 40: | Oddstring "" => 0 ! 41: | Oddstring s => size(s)-1 ! 42: | Div => 10000 ! 43: \end{verbatim} ! 44: Note that the whole expression is well-typed because in each handling ! 45: rule the type of the match-pattern is \verb"exn", and because the ! 46: result type of the match is \verb"int", the same as the type of exp. ! 47: ! 48: Note also that the last handling rule will handle \verb"Div" ! 49: exceptions raised by exp, but will not handle the \verb"Div" ! 50: exception that may be raised by \verb"x div y" in the third handling ! 51: rule. Finally, note that a universal handling rule ! 52: \begin{verbatim} ! 53: | _ => 50000 ! 54: \end{verbatim} ! 55: at the end would deal with all other exceptions raised by exp. ! 56: ! 57: \section{Exception constructors} ! 58: For an exception constructor E, the expression ! 59: \begin{quote} ! 60: $E$ \verb"(" {\it exp} \verb")" ! 61: \end{quote} ! 62: evaluates the expression {\it exp}, producing value $v$, ! 63: and then applies the constructor $E$ to it, yielding the value ! 64: $E(v)$, whose type is \verb"exn". ! 65: ! 66: The \verb"raise" keyword may be applied to any expression of type ! 67: \verb"exn", and has the effect of ``raising'' that exception value. ! 68: The innermost (dynamically) enclosing expression ! 69: $e = e_1~\verb"handle"~m_1$ is found; all further evaluation of the ! 70: expression $e_1$ (and its subphrases) is aborted; and the match $m_1$ ! 71: is applied to the exception value, yielding the result of the ! 72: expression $e$. ! 73: ! 74: If the match in a handler fails, then the exception value is ! 75: \label{reraise} ! 76: re-raised, and another enclosing handler is found. ! 77: ! 78: Exception constructors may be nullary (have no associated value), in ! 79: which case the {\it exp} and {\it pat} in the previous discussion are ! 80: omitted. ! 81: ! 82: Exceptions may be constructed independently of raising them: ! 83: \begin{verbatim} ! 84: exception A of int ! 85: val e = A 6 ! 86: val x = raise e ! 87: \end{verbatim} ! 88: Handlers may be abstracted from the \verb"handle" keyword: ! 89: \begin{verbatim} ! 90: val h = fn E 0 => "zero" ! 91: | E _ => "nonzero" ! 92: | v => raise v ! 93: ! 94: f(x) handle e => h e ! 95: \end{verbatim} ! 96: Note that it is advisable in this case to have a default clause in ! 97: the function \verb"h", since the default for a \verb"handle" match ! 98: (re-raising the exception) is different from the default for a \verb"fn" or ! 99: \verb"case" match (raising the \verb"Match" exception). ! 100: ! 101: The ordinary wildcard pattern ! 102: \verb"_" will handle any exception when it is used in a pattern, as ! 103: will any pattern consisting solely of a variable. These should be ! 104: used with some care, bearing in mind that they will even handle ! 105: interrupts. ! 106: ! 107: Nullary exception names, when misspelled, appear to the compiler to be ! 108: variables; these will then match any exception. For this reason we ! 109: recommend the convention that exception names (and other ! 110: constructors) be written beginning with an uppercase character, and ! 111: variables be written beginning with a lowercase character. The ! 112: compiler may remind the programmer of this convention when it is ! 113: violated. ! 114: ! 115: \section{Some pathological examples} ! 116: \label{pathexn} ! 117: We now consider some possible misuses of exception handling, which ! 118: may arise from the fact that exception declarations have scope, and ! 119: that each evaluation of a generative exception binding creates a ! 120: distinct exception. Consider a simple example: ! 121: \begin{verbatim} ! 122: exception E of bool ! 123: fun f(x) = ! 124: let exception E of int ! 125: in if x > 100 then raise E(x) else x+1 ! 126: end ! 127: val z = f(200) handle E(true) => 500 | E(false) => 1000 ! 128: \end{verbatim} ! 129: The program is well-typed, but useless. The exception bound to the ! 130: outer \verb"E" is distinct from that bound to the inner \verb"E"; ! 131: thus the exception raised by \verb"f(200)", with excepted value 200, ! 132: could only be handled by a handler within the scope of the inner ! 133: exception declaration---it will not be handled by the handler in the ! 134: program, which expects a boolean value. So this exception will be ! 135: reported at top level. This would apply even if the outer exception ! 136: declaration were also of type int; the two exceptions bound to ! 137: \verb"E" would be distinct. ! 138: ! 139: On the other hand, if the last line of the program is changed to ! 140: \begin{verbatim} ! 141: f(200) handle _ => 500 ! 142: \end{verbatim} ! 143: then the exception will be caught, and the value 500 returned. A ! 144: universal handling rule (i.e. \verb"_" or a variable-identifier) ! 145: catches any exception---even one exported from the scope of the ! 146: declaration of the associated exception name---but cannot examine the ! 147: excepted value carried by the exception constructor, since the type ! 148: of this value cannot be statically determined. ! 149: ! 150: Even a single textual exception binding---if for example it is ! 151: declared within a recursively defined function---may bind distinct ! 152: exceptions to the same identifier. Consider another useless program: ! 153: \begin{verbatim} ! 154: fun f(x) = ! 155: let exception E ! 156: in if p(x) then a(x) ! 157: else if q(x) then f(b(x)) handle E => c(x) ! 158: else raise E ! 159: end ! 160: val z = f v ! 161: \end{verbatim} ! 162: Now if p(v) is false but q(v) is true, the recursive call will ! 163: evaluate f(b(v)). Then if both p(b(v) and q(b(v)) are false, this ! 164: evaluation will raise \verb"E". But this exception will not be ! 165: handled, since the exception raised is that which is bound to ! 166: \verb"E" by the inner---not outer---evaluation of the exception ! 167: declaration. ! 168: ! 169: These pathological examples should not leave the impression that ! 170: exceptions are hard to use or to understand. The rough and ready ! 171: rule of Section~\ref{exception} will almost always give the correct ! 172: understanding.
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.