1068 |
1068 |
1069 section \<open>Exceptions \label{sec:exceptions}\<close> |
1069 section \<open>Exceptions \label{sec:exceptions}\<close> |
1070 |
1070 |
1071 text \<open> |
1071 text \<open> |
1072 The Standard ML semantics of strict functional evaluation together with |
1072 The Standard ML semantics of strict functional evaluation together with |
1073 exceptions is rather well defined, but some delicate points need to be |
1073 exceptions is rather well defined, but some fine points need to be observed |
1074 observed to avoid that ML programs go wrong despite static type-checking. |
1074 to avoid that ML programs go wrong despite static type-checking. |
|
1075 |
|
1076 Unlike official Standard ML, Isabelle/ML rejects catch-all patterns in |
|
1077 \<^ML_text>\<open>handle\<close> clauses: this improves the robustness of ML programs, |
|
1078 especially against arbitrary physical events (interrupts). |
|
1079 |
1075 Exceptions in Isabelle/ML are subsequently categorized as follows. |
1080 Exceptions in Isabelle/ML are subsequently categorized as follows. |
1076 \<close> |
1081 \<close> |
1077 |
1082 |
1078 paragraph \<open>Regular user errors.\<close> |
1083 paragraph \<open>Regular user errors.\<close> |
1079 text \<open> |
1084 text \<open> |
1098 \<close> |
1103 \<close> |
1099 |
1104 |
1100 paragraph \<open>Program failures.\<close> |
1105 paragraph \<open>Program failures.\<close> |
1101 text \<open> |
1106 text \<open> |
1102 There is a handful of standard exceptions that indicate general failure |
1107 There is a handful of standard exceptions that indicate general failure |
1103 situations, or failures of core operations on logical entities (types, |
1108 situations (e.g.\ \<^ML>\<open>Fail\<close>), or failures of core operations on logical |
1104 terms, theorems, theories, see \chref{ch:logic}). |
1109 entities (types, terms, theorems, theories, see \chref{ch:logic}). |
1105 |
1110 |
1106 These exceptions indicate a genuine breakdown of the program, so the main |
1111 These exceptions indicate a genuine breakdown of the program, so the main |
1107 purpose is to determine quickly what has happened where. Traditionally, the |
1112 purpose is to determine quickly what has happened in the ML program. |
1108 (short) exception message would include the name of an ML function, although |
1113 Traditionally, the (short) exception message would include the name of an ML |
1109 this is not strictly necessary, because the ML runtime system attaches |
1114 function, although this is not strictly necessary, because the ML runtime |
1110 detailed source position stemming from the corresponding \<^ML_text>\<open>raise\<close> |
1115 system attaches detailed source position stemming from the corresponding |
1111 keyword. |
1116 \<^ML_text>\<open>raise\<close> keyword. |
1112 |
1117 |
1113 \<^medskip> |
1118 \<^medskip> |
1114 User modules can always introduce their own custom exceptions locally, e.g.\ |
1119 User modules can always introduce their own custom exceptions locally, e.g.\ |
1115 to organize internal failures robustly without overlapping with existing |
1120 to organize internal failures robustly without overlapping with existing |
1116 exceptions. Exceptions that are exposed in module signatures require extra |
1121 exceptions. Exceptions that are exposed in module signatures require extra |
1127 |
1132 |
1128 This is the one and only way that physical events can intrude an Isabelle/ML |
1133 This is the one and only way that physical events can intrude an Isabelle/ML |
1129 program. Such an interrupt can mean out-of-memory, stack overflow, timeout, |
1134 program. Such an interrupt can mean out-of-memory, stack overflow, timeout, |
1130 internal signaling of threads, or a POSIX process signal. An Isabelle/ML |
1135 internal signaling of threads, or a POSIX process signal. An Isabelle/ML |
1131 program that intercepts interrupts becomes dependent on physical effects of |
1136 program that intercepts interrupts becomes dependent on physical effects of |
1132 the environment. Even worse, exception handling patterns that are too |
1137 the environment (e.g.\ via \<^ML>\<open>Exn.capture\<close> without subsequent |
1133 general by accident, e.g.\ by misspelled exception constructors, will cover |
1138 \<^ML>\<open>Exn.release\<close>). |
1134 interrupts unintentionally and thus render the program semantics |
1139 |
1135 ill-defined. |
1140 Note that the original SML90 language had an \<^verbatim>\<open>Interrupt\<close> exception, but |
1136 |
1141 that was excluded from SML97 to simplify ML the mathematical semantics. |
1137 Note that the Interrupt exception dates back to the original SML90 language |
1142 Isabelle/ML does support physical interrupts thanks to special features of |
1138 definition. It was excluded from the SML97 version to avoid its malign |
1143 the underlying Poly/ML compiler and runtime system. This works robustly, |
1139 impact on ML program semantics, but without providing a viable alternative. |
1144 because the old \<^ML_text>\<open>Interrupt\<close> constructor has been removed from the |
1140 Isabelle/ML recovers physical interruptibility (which is an indispensable |
1145 ML environment, and catch-all patterns \<^ML_text>\<open>handle\<close> are rejected. |
1141 tool to implement managed evaluation of command transactions), but requires |
1146 Thus user code becomes strictly transparent wrt.\ interrupts: physical |
1142 user code to be strictly transparent wrt.\ interrupts. |
1147 events are exposed to the toplevel, and the mathematical meaning of the |
1143 |
1148 program becomes a partial function that is otherwise unchanged. |
1144 \begin{warn} |
1149 |
1145 Isabelle/ML user code needs to terminate promptly on interruption, without |
1150 The Isabelle/ML antiquotation @{ML_antiquotation try}, with its syntactic |
1146 guessing at its meaning to the system infrastructure. Temporary handling of |
1151 variants for \<^ML_text>\<open>catch\<close> or \<^ML_text>\<open>finally\<close>, supports |
1147 interrupts for cleanup of global resources etc.\ needs to be followed |
1152 intermediate handling of interrupts and subsequent cleanup-operations, |
1148 immediately by re-raising of the original exception. |
1153 without swallowing such physical event. |
1149 \end{warn} |
|
1150 \<close> |
1154 \<close> |
1151 |
1155 |
1152 text %mlref \<open> |
1156 text %mlref \<open> |
1153 \begin{mldecls} |
1157 \begin{mldecls} |
1154 @{define_ML try: "('a -> 'b) -> 'a -> 'b option"} \\ |
1158 @{define_ML try: "('a -> 'b) -> 'a -> 'b option"} \\ |
1158 @{define_ML Exn.is_interrupt: "exn -> bool"} \\ |
1162 @{define_ML Exn.is_interrupt: "exn -> bool"} \\ |
1159 @{define_ML Exn.reraise: "exn -> 'a"} \\ |
1163 @{define_ML Exn.reraise: "exn -> 'a"} \\ |
1160 @{define_ML Runtime.exn_trace: "(unit -> 'a) -> 'a"} \\ |
1164 @{define_ML Runtime.exn_trace: "(unit -> 'a) -> 'a"} \\ |
1161 \end{mldecls} |
1165 \end{mldecls} |
1162 |
1166 |
1163 \<^descr> \<^ML>\<open>try\<close>~\<open>f x\<close> makes the partiality of evaluating \<open>f x\<close> explicit via the |
1167 \<^descr> \<^ML>\<open>try\<close>~\<open>f x\<close> makes the partiality of evaluating \<open>f x\<close> explicit via |
1164 option datatype. Interrupts are \<^emph>\<open>not\<close> handled here, i.e.\ this form serves |
1168 the option datatype. Interrupts are \<^emph>\<open>not\<close> handled here, i.e.\ this form |
1165 as safe replacement for the \<^emph>\<open>unsafe\<close> version \<^ML_text>\<open>(SOME\<close>~\<open>f |
1169 serves as safe replacement for the \<^emph>\<open>fragile\<close> version \<^ML_text>\<open>(SOME\<close>~\<open>f |
1166 x\<close>~\<^ML_text>\<open>handle _ => NONE)\<close> that is occasionally seen in books about |
1170 x\<close>~\<^ML_text>\<open>handle _ => NONE)\<close> that is occasionally seen in books about |
1167 SML97, but not in Isabelle/ML. |
1171 SML97. |
1168 |
1172 |
1169 \<^descr> \<^ML>\<open>can\<close> is similar to \<^ML>\<open>try\<close> with more abstract result. |
1173 \<^descr> \<^ML>\<open>can\<close> is similar to \<^ML>\<open>try\<close> with more abstract result. |
1170 |
1174 |
1171 \<^descr> \<^ML>\<open>ERROR\<close>~\<open>msg\<close> represents user errors; this exception is normally |
1175 \<^descr> \<^ML>\<open>ERROR\<close>~\<open>msg\<close> represents user errors; this exception is normally |
1172 raised indirectly via the \<^ML>\<open>error\<close> function (see |
1176 raised indirectly via the \<^ML>\<open>error\<close> function (see |
1173 \secref{sec:message-channels}). |
1177 \secref{sec:message-channels}). |
1174 |
1178 |
1175 \<^descr> \<^ML>\<open>Fail\<close>~\<open>msg\<close> represents general program failures. |
1179 \<^descr> \<^ML>\<open>Fail\<close>~\<open>msg\<close> represents general program failures, but not user errors. |
1176 |
1180 |
1177 \<^descr> \<^ML>\<open>Exn.is_interrupt\<close> identifies interrupts robustly, without mentioning |
1181 \<^descr> \<^ML>\<open>Exn.is_interrupt\<close> identifies interrupts, without mentioning |
1178 concrete exception constructors in user code. Handled interrupts need to be |
1182 concrete exception constructors in user code. Since \<^ML_text>\<open>handle\<close> with |
1179 re-raised promptly! |
1183 catch-all patterns is rejected, it cannot handle interrupts at all. In the |
|
1184 rare situations where this is really required, use \<^ML>\<open>Exn.capture\<close> and |
|
1185 \<^ML>\<open>Exn.release\<close> (\secref{sec:managed-eval}). |
1180 |
1186 |
1181 \<^descr> \<^ML>\<open>Exn.reraise\<close>~\<open>exn\<close> raises exception \<open>exn\<close> while preserving its implicit |
1187 \<^descr> \<^ML>\<open>Exn.reraise\<close>~\<open>exn\<close> raises exception \<open>exn\<close> while preserving its implicit |
1182 position information (if possible, depending on the ML platform). |
1188 position information (if possible, depending on the ML platform). |
1183 |
1189 |
1184 \<^descr> \<^ML>\<open>Runtime.exn_trace\<close>~\<^ML_text>\<open>(fn () =>\<close>~\<open>e\<close>\<^ML_text>\<open>)\<close> evaluates |
1190 \<^descr> \<^ML>\<open>Runtime.exn_trace\<close>~\<^ML_text>\<open>(fn () =>\<close>~\<open>e\<close>\<^ML_text>\<open>)\<close> evaluates |
1199 |
1205 |
1200 \<^rail>\<open> |
1206 \<^rail>\<open> |
1201 (@@{ML_antiquotation try} | @@{ML_antiquotation can}) embedded |
1207 (@@{ML_antiquotation try} | @@{ML_antiquotation can}) embedded |
1202 \<close> |
1208 \<close> |
1203 |
1209 |
1204 \<^descr> \<open>@{try}\<close> and \<open>{can}\<close> are similar to the corresponding functions, but the |
1210 |
1205 argument is taken directly as ML expression: functional abstraction and |
1211 \<^descr> \<open>@{try}\<close> and \<open>{can}\<close> take embedded ML source as arguments, and modify the |
1206 application is done implicitly. |
1212 evaluation analogously to the combinators \<^ML>\<open>try\<close> and \<^ML>\<open>can\<close> above, |
|
1213 but with special treatment of the interrupt state of the underlying ML |
|
1214 thread. There are also variants to support \<^verbatim>\<open>try_catch\<close> and \<^verbatim>\<open>try_finally\<close> |
|
1215 blocks similar to Scala. |
|
1216 |
|
1217 The substructure of the embedded argument supports the following syntax |
|
1218 variants: |
|
1219 |
|
1220 \<^rail>\<open> |
|
1221 @{syntax_def try_catch}: @{syntax expr} @'catch' @{syntax handler}; |
|
1222 @{syntax_def try_finally}: @{syntax expr} @'finally' @{syntax cleanup}; |
|
1223 @{syntax_def try}: @{syntax expr}; |
|
1224 @{syntax_def can}: @{syntax expr} |
|
1225 \<close> |
|
1226 |
|
1227 The @{syntax handler} of \<^verbatim>\<open>try_catch\<close> follows the syntax of \<^ML_text>\<open>fn\<close> |
|
1228 patterns, so it is similar to \<^ML_text>\<open>handle\<close>: the key difference is |
|
1229 that interrupts are always passed-through via \<^ML>\<open>Exn.reraise\<close>. |
|
1230 |
|
1231 The @{syntax cleanup} expression of \<^verbatim>\<open>try_finally\<close> is always invoked, |
|
1232 regardless of the overall exception result, and afterwards exceptions are |
|
1233 passed-through via \<^ML>\<open>Exn.reraise\<close>. |
|
1234 |
|
1235 Both the @{syntax handler} and @{syntax cleanup} are evaluated with further |
|
1236 interrupts disabled! These expressions should terminate promptly; timeouts |
|
1237 don't work here. |
|
1238 |
|
1239 \<^medskip> |
|
1240 Implementation details can be seen in \<^ML>\<open>Isabelle_Thread.try_catch\<close>, |
|
1241 \<^ML>\<open>Isabelle_Thread.try_finally\<close>, \<^ML>\<open>Isabelle_Thread.try\<close>, and |
|
1242 \<^ML>\<open>Isabelle_Thread.can\<close>, respectively. The ML antiquotations add |
|
1243 functional abstractions as required for these ``special forms'' of |
|
1244 Isabelle/ML. |
1207 |
1245 |
1208 \<^descr> \<open>@{assert}\<close> inlines a function \<^ML_type>\<open>bool -> unit\<close> that raises |
1246 \<^descr> \<open>@{assert}\<close> inlines a function \<^ML_type>\<open>bool -> unit\<close> that raises |
1209 \<^ML>\<open>Fail\<close> if the argument is \<^ML>\<open>false\<close>. Due to inlining the source |
1247 \<^ML>\<open>Fail\<close> if the argument is \<^ML>\<open>false\<close>. Due to inlining the source |
1210 position of failed assertions is included in the error output. |
1248 position of failed assertions is included in the error output. |
1211 |
1249 |
1212 \<^descr> \<open>@{undefined}\<close> inlines \<^verbatim>\<open>raise\<close>~\<^ML>\<open>Match\<close>, i.e.\ the ML program |
1250 \<^descr> \<open>@{undefined}\<close> inlines \<^verbatim>\<open>raise\<close>~\<^ML>\<open>Match\<close>, i.e.\ the ML program |
1213 behaves as in some function application of an undefined case. |
1251 behaves as in some function application of an undefined case. |
1214 \<close> |
1252 \<close> |
1215 |
1253 |
1216 text %mlex \<open> |
1254 text %mlex \<open> |
1217 We define a total version of division: any failures are swept under the |
1255 |
|
1256 We define total versions of division: any failures are swept under the |
1218 carpet and mapped to a default value. Thus division-by-zero becomes 0, but |
1257 carpet and mapped to a default value. Thus division-by-zero becomes 0, but |
1219 there could be other exceptions like overflow that produce the same result |
1258 there could be other exceptions like overflow that produce the same result. |
1220 (for unbounded integers this does not happen). |
1259 |
|
1260 For unbounded integers such side-errors do not happen, but it might still be |
|
1261 better to be explicit about exception patterns (second version below). |
1221 \<close> |
1262 \<close> |
1222 |
1263 |
1223 ML \<open> |
1264 ML \<open> |
1224 fun div_total x y = \<^try>\<open>x div y\<close> |> the_default 0; |
1265 fun div_total1 x y = \<^try>\<open>x div y catch _ => 0\<close>; |
1225 |
1266 fun div_total2 x y = \<^try>\<open>x div y catch Div => 0\<close>; |
1226 \<^assert> (div_total 1 0 = 0); |
1267 |
|
1268 \<^assert> (div_total1 1 0 = 0); |
|
1269 \<^assert> (div_total2 1 0 = 0); |
1227 \<close> |
1270 \<close> |
1228 |
1271 |
1229 text \<open> |
1272 text \<open> |
1230 The ML function \<^ML>\<open>undefined\<close> is defined in \<^file>\<open>~~/src/Pure/library.ML\<close> |
1273 The ML function \<^ML>\<open>undefined\<close> is defined in \<^file>\<open>~~/src/Pure/library.ML\<close> |
1231 as follows: |
1274 as follows: |