Docs for error handling
This commit is contained in:
parent
2b97f75188
commit
9fa2d8caa3
@ -17,138 +17,152 @@ For example, a function that returned a `DivisionByZero`
|
|||||||
may be written like this:
|
may be written like this:
|
||||||
|
|
||||||
```thp
|
```thp
|
||||||
fun invert(Int number) -> Result[Int, DivisonByZero]
|
fun invert(Int number) -> Int!DivisionByZero
|
||||||
{
|
{
|
||||||
if number == 0
|
if number == 0
|
||||||
{
|
{
|
||||||
return Err(DivisionByZero())
|
return DivisionByZero()
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(1 / number)
|
return 1 / number
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In the previous segment, `Result[Int, DivisionByZero]` denotates
|
In the previous segment, `Int!DivisionByZero` denotates
|
||||||
that the function may return either an `Int` or an `DivisionByZero`.
|
that the function may return either an `Int` or an `DivisionByZero`.
|
||||||
|
|
||||||
To return the error we use `Err(...)`, and to return the succes value
|
We then can return the error or success value;
|
||||||
we use `Ok(...)`.
|
|
||||||
|
|
||||||
The error may be of any datatype.
|
|
||||||
|
|
||||||
|
|
||||||
### Multiple error returns
|
### Multiple error returns
|
||||||
|
|
||||||
|
TODO: fix?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
If there are multiple error types that the function can return,
|
If there are multiple error types that the function can return,
|
||||||
you can use the `|` operator:
|
you can use the `|` operator:
|
||||||
|
|
||||||
```thp
|
```thp
|
||||||
fun sample() -> Result[Int, Exception1|Exception2|Exception3]
|
type Exceptions = Exception1 | Exception2 | Exception3
|
||||||
|
|
||||||
|
fun sample() -> Int!Exceptions
|
||||||
{ /* ... */}
|
{ /* ... */}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Handle an error
|
## Error handling
|
||||||
|
|
||||||
Since `Result` is an enum we can use pattern matching to
|
The caller must handle all possible errors, they don't automatically
|
||||||
get the ok value, or handle the error:
|
bubble up the stack.
|
||||||
|
|
||||||
```thp
|
THP provides syntax for handling errors following certain patterns,
|
||||||
val result = match inverse(5)
|
via try expressions:
|
||||||
case ::Ok(value) { value }
|
|
||||||
case ::Err(error) { return error }
|
|
||||||
```
|
|
||||||
|
|
||||||
However, THP provides syntactic sugar for many common
|
|
||||||
patterns for error handling.
|
|
||||||
|
|
||||||
## Try expressions
|
|
||||||
|
|
||||||
There are several try expressions that simplify error handling:
|
|
||||||
|
|
||||||
### Naked try
|
### Naked try
|
||||||
|
|
||||||
Using a `try` followed by an expression will execute the
|
Use a naked `try` when you want to rethrow an error, if any.
|
||||||
expression, and if `Ok` is returned, it will return it's value.
|
|
||||||
If `Err` is returned, the error will be re-thrown.
|
|
||||||
|
|
||||||
```thp
|
```thp
|
||||||
fun sample(Int x) -> Result[Int, DivisionByZero]
|
// May return an Int or an Exception
|
||||||
{
|
fun dangerous() -> Int!Exception
|
||||||
val result = try inverse(x)
|
{...}
|
||||||
|
|
||||||
result
|
fun run() -> !Exception
|
||||||
|
{
|
||||||
|
// Use a naked `try` to rethrow if there's an error
|
||||||
|
val result = try dangerous()
|
||||||
|
// Here result is `Int`
|
||||||
|
print(result)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If `inverse(x)` fails, the error will be returned again.
|
In the previous example:
|
||||||
|
|
||||||
If `inverse(x)` succeedes, its value will be assigned to `result`.
|
- If `dangerous()` returns an `Exception`, this exception
|
||||||
|
will be returned by `run()`;
|
||||||
|
- If `dangerous()` succeedes, its value is assigned
|
||||||
|
to `result`, and the function continues executing.
|
||||||
|
|
||||||
|
|
||||||
### Try/return
|
### Try/return
|
||||||
|
|
||||||
|
Try/return will return a new value if an expression fails,
|
||||||
|
otherwise will assign the success value and continue.
|
||||||
|
|
||||||
Try/return will run a function and assign its value if `Ok` is found.
|
Try/return will run a function and assign its value if `Ok` is found.
|
||||||
Otherwise, it will return a new value specified by the programmer.
|
Otherwise, it will return a new value specified by the programmer.
|
||||||
|
|
||||||
```thp
|
```thp
|
||||||
fun sample(Int x) -> String
|
fun run() -> Int
|
||||||
{
|
{
|
||||||
val result = try inverse(x) return "0 was found"
|
val result = try dangerous() return 0
|
||||||
|
|
||||||
if result == 2 { "2 was found" }
|
// ...
|
||||||
else { "other number was found" }
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If `inverse(x)` fails, `"0 was found"` will be returned.
|
In the previous example:
|
||||||
|
|
||||||
If `inverse(x)` succeedes, its value will be assigned to `result`
|
- If `dangerous()` fails, its error will be ignored, and `0` will
|
||||||
and the code will continue to execute normally.
|
be returned from `run()`.
|
||||||
|
- If `dangerous()` succeedes, its value will be assigned to `result`,
|
||||||
|
and the function continues executing.
|
||||||
|
|
||||||
|
|
||||||
### Try/else
|
### Try/else
|
||||||
|
|
||||||
Try/else will run an expression and assign its value if `Ok`.
|
Try/return will assign a new value if an expression fails.
|
||||||
Otherwise it will assign a second value.
|
|
||||||
|
|
||||||
```thp
|
```thp
|
||||||
fun sample(Int x) -> Int
|
fun run()
|
||||||
{
|
{
|
||||||
val result = try inverse(x) else 0.0
|
val result = try dangerous() else 322
|
||||||
|
|
||||||
result
|
print(result)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If `inverse(x)` fails, `0.0` will be assigned to `result`.
|
- If `dangerous()` fails, the value `322` will be assigned to `result`.
|
||||||
|
- If `dangerous()` succeedes, its value will be assigned to `result`.
|
||||||
|
|
||||||
If `inverse(x)` succeedes, its value will be assigned to `result`.
|
Either way, the function will continue executing.
|
||||||
|
|
||||||
|
|
||||||
### Try/catch
|
### Try/catch
|
||||||
|
|
||||||
Try/catch will run an expression and assign its value if `Ok`.
|
Try/catch allows the error to be manually used & handled.
|
||||||
Otherwise it will run a block of code, which will handle
|
|
||||||
the error and assign a value as well.
|
|
||||||
|
|
||||||
```thp
|
```thp
|
||||||
fun sample(Int x)
|
fun run()
|
||||||
{
|
{
|
||||||
val result = try inverse(x)
|
val result = try dangerous()
|
||||||
catch DivisionByZero e
|
catch Exception e
|
||||||
{
|
{
|
||||||
// Handle `e`
|
// This is run if `dangerous()` throws.
|
||||||
|
// `e` is the thrown error
|
||||||
|
|
||||||
|
// Handle the error
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
0.0
|
// Return a new value to be assigned to `result`
|
||||||
|
0
|
||||||
}
|
}
|
||||||
catch Exception e
|
|
||||||
{ ... }
|
|
||||||
catch Error e
|
|
||||||
{ ... }
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
A try/catch may have many `catch` clauses:
|
||||||
|
|
||||||
|
```thp
|
||||||
|
try dangerous()
|
||||||
|
catch Exception1 e
|
||||||
|
{...}
|
||||||
|
catch Exception2 e
|
||||||
|
{...}
|
||||||
|
catch Exception3 e
|
||||||
|
{...}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user