layout | title | category | tags | order | |||
---|---|---|---|---|---|---|---|
developer-doc |
Wrapped Errors |
semantics |
|
12 |
A wrapped error is an error wrapped in an additional 'error wrapper' value providing additional information about the error.
For example, an error attached to a value within a Vector
can be wrapped in a
Map_Error
wrapper which indicates the position of the value within the
Vector
.
(Such errors are only wrapped when they are obtained through
Warning.get_all wrap_errors=True
; see
Obtaining Wrapped Errors.)
For example:
err = My_Error.Error "my error"
vec = [10, Warning.attach err 20, 30]
IO.println (Warning.get_all vec)
IO.println (Warning.get_all wrap_errors=True vec)
Output:
[My_Error.Error my error]
[Map_Error.Error 1 (My_Error.Error my error)] # The error is at index 1
Wrapped errors are "transparent" to Error.catch
. That is, if you attempt to
catch a certain error, but the error that is actually thrown is wrapped, the
catch will still succeed. In the example below, a My_Error
error is thrown in
a call to map
, and so the resulting error is wrapped. You can catch the error
as a My_Error
, or as a Map_Error
.
fun x = if x == 20 then Error.throw (My_Error.Error "my error") else x
[10, 20, 30].map fun . catch My_Error e->
IO.println e
[10, 20, 30].map fun . catch Map_Error e->
IO.println e
Output:
(My_Error.Error 'my error')
(Map_Error.Error 1 (My_Error.Error 'my error'))
Note that if you catch the error as the inner error (My_Error
), the
Map_Error
wrapper is stripped off.
An error wrapper is a regular Enso value that has a conversion to
Wrapped_Error
. For example:
type Map_Error
Error (index:Integer) inner_error
Wrapped_Error.from (that : Map_Error) = Wrapped_Error.Value that that.inner_error
The from
implementation allows Error.catch
to detect that it is an error
wrapper, and possibly perform automatic unwrapping on it.
Wrapped errors are obtained in two ways:
- An error thrown during a call to
Vector.map
- An error attached to a value within a
Vector
In the case of an error thrown during Vector.map
, the error is caught, wrapped
in Map_Error
, and re-thrown.
In the case of an error attached to a value within a Vector
, the wrapper is
added by Warning.get_all wrap_errors=True
when it is called on the Vector
.
In this case, the wrapping is not attached to the value itself, and is therefore
not propagated to downstream values.
Map_Error
is the motivating example for wrapped errors, and is currently the
only implemented error wrapper. It exists so that an error thrown during a map
call over a large Vector
will contain the index at which the error occurred,
so it can be displayed to the user in the IDE.
Note that the error does not have to occur during a call to map
. A Map_Error
wrapping is added by Warning.get_all wrap_errors=True
to any error attached to
a value within a Vector
(or any array-like container). If the value is
extracted from the Vector
(for example, using .at
), its attached Warning
is not wrapped.
If a value is nested within multiple Vector
s, its attached errors are wrapped
with Map_Error
multiple times. The outermost Map_Error
index indicates the
index into the outermost Vector
, the second Map_Error
index the index into
the sub-Vector
within the outermost Vector
, and so on.
For example:
fun a = if a == 30 then Error.throw (My_Error.Error a) else a+1
nested_vector = [[10, 20, 30, 40], [30, 10, 20, 30]]
result = nested_vector.map (_.map fun on_problems=Problem_Behavior.Report_Warning) on_problems=Problem_Behavior.Report_Warning
warnings = Warning.get_all wrap_errors=True result . map .value
warnings.map w->
IO.println w
Output:
(Map_Error.Error 1 (Map_Error.Error 3 (My_Error.Error 30))) # [1, 3]
(Map_Error.Error 1 (Map_Error.Error 0 (My_Error.Error 30))) # [1, 0]
(Map_Error.Error 0 (Map_Error.Error 2 (My_Error.Error 30))) # [0, 2]
The following test utilities take an unwrap_errors
parameter:
Error.should_fail_with
Problem.test_problem_handling
Problem.expect_warning
Problem.expect_only_warning
By default, these methods will automatically unwrap errors. Passing
unwrap_errors=False
will disable this behavior.