Exceptions: Difference between revisions

4,439 bytes added ,  15 years ago
add E example, at length
m (change include to be prelude)
(add E example, at length)
Line 309:
test2();
}</lang>
 
=={{header|E}}==
 
===Exceptions===
 
An exception ''object'' describes what the problem is and has nothing to do with control flow.
 
Due to E's ancestry as a JVM scripting language, E does not '''yet''' have any standard mechanism for user-defined exception types.
 
A string provided in place of an exception will be coerced to a generic exception object.
 
There are two control flow constructs used with exceptions: throw and eject.
 
===Throw and catch===
 
<code>throw</code> is the built-in ''function'' which throws exceptions in the conventional sense: control goes to the <code>catch</code> block of the most recently entered <code>try</code>/<code>catch</code> construct.
 
<lang e>
def nameOf(arg :int) {
if (arg == 43) {
return "Bob"
} else {
throw("Who?")
}
}
 
def catching(arg) {
try {
return ["ok", nameOf(arg)]
} catch exceptionObj {
return ["notok", exceptionObj]
}
}</lang>
 
<lang e>? catching(42)
# value: ["not ok", problem: Who?]
 
? catching(43)
# value: ["ok", "Bob"]
 
? catching(45.7)
# value: ["not ok", problem: the float64 45.7 doesn't coerce to an int]</lang>
 
However, there is a problem here: exceptions accidentally produced or uncaught from inside a given module can lead to the calling program getting information about the internals that it shouldn't have (possibly a security problem). As a result of this, we are planning to move to a 'sealed exception' model where throw and catch have the same control flow, but only debuggers can see any information in a ''caught'' exception other than "a throw happened". For situations where the caller ''should'' have information about what happened, the ejector mechanism will be used.
 
===Ejectors===
 
Ejectors provide the same sort of "exit to catch block" control flow that throw/catch do, but with an explicit path rather than implicitly "nearest enclosing". Ejectors are also used as a general purpose control construct as well as for exceptions.
 
The <code>escape ''ej'' { ''body'' } catch ''pat'' { ''catch block'' }</code> construct creates an ejector object and binds it to ''ej'', which is valid for as long as ''body'' is executing. An ejector object is a function; if it is called, then control immediately passes to the ''catch block'', with its argument bound to ''pat''.
 
The above code rewritten to use ejectors:
 
<lang e>def nameOf(arg :int, ejector) {
if (arg == 43) {
return "Bob"
} else {
ejector("Who?")
}
}
 
def catching(arg) {
escape unnamed {
return ["ok", nameOf(arg, unnamed)]
} catch exceptionObj {
return ["notok", exceptionObj]
}
}
</lang>
 
<lang e>? catching(42)
# value: ["not ok", problem: Who?]
 
? catching(43)
# value: ["ok", "Bob"]
 
? catching(45.7)
# problem: the float64 45.7 doesn't coerce to an int</lang>
 
Note that the escape-catch block does ''not'' catch the coercion error resulting from passing a float64 instead of an int, since that is an (implicit) throw.
 
(One further refinement: While an ejector is an ordinary function, which does not return, it is generally desirable to protect against being supplied a function which unexpectedly ''does'' return. For this purpose we have <code>throw.eject</code> which calls the supplied function and throws if that function returns: <code>throw.eject(ejector, "Who?")</code>)
 
The benefit of using ejectors to communicate exceptions, besides the information-leak prevention described above, is that only exceptions intended to be handled by that catch block will be passed to it; unexpected internal errors will be handled by general try/catch handlers.
 
For example, suppose we have nameOf written as follows:
 
<lang e>var nameTable := null
def nameOf(arg :int, ejector) {
if (nameTable == null) {
nameTable := <import:nameTableParser>.parseFile(<file:nameTable.txt>)
}
if (nameTable.maps(arg)) {
return nameTable[arg]
} else {
ejector(makeNotFoundException("Who?"))
}
}</lang>
 
Suppose that loading the parser, or reading the file, throws a NotFoundException (note this exception type was made up for this example). Even though it is of the same type as the "Who?" exception, it will not be caught by the caller's escape/catch block since it was not passed via the ejector, whereas a traditional "try { ... } catch ex :NotFoundException { ... }" as in other languages would, leading to incorrect handling of the error.
 
=={{header|Factor}}==
Line 334 ⟶ 434:
[ "Hi" print ] catch ! returns f (looks the same as throwing f; don't throw f)
[ f throw ] catch ! returns f, bad! use recover or cleanup instead
 
=={{header|Forth}}==
Forth's exception mechanism is, like most things in Forth, very simple but powerful. CATCH captures the data and return stack pointers, then executes an execution token. THROW conditionally throws a value up to the most recent CATCH, restoring the stack pointers.