mirror of
https://github.com/enso-org/enso.git
synced 2024-12-25 13:42:33 +03:00
142 lines
4.4 KiB
Markdown
142 lines
4.4 KiB
Markdown
|
---
|
||
|
layout: developer-doc
|
||
|
title: Wrapped Errors
|
||
|
category: semantics
|
||
|
tags: [semantics, errors, runtime]
|
||
|
order: 12
|
||
|
---
|
||
|
|
||
|
# Wrapped Errors
|
||
|
|
||
|
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](#obtaining-wrapped-errors).)
|
||
|
|
||
|
For example:
|
||
|
|
||
|
```ruby
|
||
|
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:
|
||
|
|
||
|
```ruby
|
||
|
[My_Error.Error my error]
|
||
|
[Map_Error.Error 1 (My_Error.Error my error)] # The error is at index 1
|
||
|
```
|
||
|
|
||
|
## Catching Wrapped Errors
|
||
|
|
||
|
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`.
|
||
|
|
||
|
```ruby
|
||
|
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:
|
||
|
|
||
|
```ruby
|
||
|
(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.
|
||
|
|
||
|
## Implementing Error Wrappers
|
||
|
|
||
|
An error wrapper is a regular Enso value that has a conversion to
|
||
|
`Wrapped_Error`. For example:
|
||
|
|
||
|
```ruby
|
||
|
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.
|
||
|
|
||
|
## Obtaining Wrapped Errors
|
||
|
|
||
|
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
|
||
|
|
||
|
`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:
|
||
|
|
||
|
```ruby
|
||
|
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:
|
||
|
|
||
|
```ruby
|
||
|
(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]
|
||
|
```
|
||
|
|
||
|
## Testing Wrapped Errors
|
||
|
|
||
|
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.
|