Skip to content

Commit

Permalink
Apply suggestions from code review
Browse files Browse the repository at this point in the history
Co-authored-by: Maryam Ziyad <maryamziyadm@gmail.com>
  • Loading branch information
heshanpadmasiri and MaryamZi committed Oct 14, 2024
1 parent 7a8ecfa commit 686a4ae
Show file tree
Hide file tree
Showing 12 changed files with 64 additions and 50 deletions.
5 changes: 3 additions & 2 deletions examples/check-expression/check_expression.bal
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import ballerina/io;

// Same as `intFromBytesWithCheck` but with explicit error handling.
function intFromBytes(byte[] bytes) returns int|error {
string|error res = string:fromBytes(bytes);
// Handling the error explicitly.
// Explicitly check if the result is an error and
// immediately return if so.
if res is error {
return res;
}
return int:fromString(res);
}

// Same as `intFromBytes` but with `check` instead of explicitly checking for error and returning.
function intFromBytesWithCheck(byte[] bytes) returns int|error {
string str = check string:fromBytes(bytes);
return int:fromString(str);
Expand Down
2 changes: 1 addition & 1 deletion examples/check-expression/check_expression.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Check expression

If an expression can cause an `error`, you can use the `check` expression to indicate you want to terminate the execution of the current scope with that error as the result. Generally this is done by returning the error value from the current function.
If an expression can evaluate to an error value, you can use the `check` expression to indicate that you want the execution of the current block to terminate with that error as the result. This results in the error value being returned from the current function/worker, unless the `check` expression is used in a failure-handling statement (e.g., statement with on fail, retry statement).

::: code check_expression.bal :::

Expand Down
1 change: 0 additions & 1 deletion examples/check-expression/check_expression.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
$ bal run check_expression.bal
error("{ballerina/lang.int}NumberParsingError",message="'string' value 'hello' cannot be converted to 'int'")
error("{ballerina/lang.int}NumberParsingError",message="'string' value 'hello' cannot be converted to 'int'")

42 changes: 23 additions & 19 deletions examples/error-reporting/error_reporting.bal
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,51 @@ type Person record {

function validatePeople(Person[] people) returns error? {
if people.length() == 0 {
// Create error with only a message.
return error("empty people array");
// Create an error value specifying only the error message.
return error("Expected a non-empty array");
}

foreach Person p in people {
io:println("Validating ", p.name);
error? err = validatePerson(p);
if err != () {
// Create a new error with previous error as the cause and people in the detail.
return error("failed to validate people", err, people = people);
if err is error {
// Create a new error value with the validation error as the cause
// and the `Person` value for which validation failed in the detail mapping.
return error("Validation failed for a person", err, person = p);
}
}
}

function validatePerson(Person person) returns error? {
if person.age < 0 {
// Create a new error we person in the detail to help debugging.
return error("age cannot be negative", person = person);
int age = person.age;
if age < 0 {
// If validation fails for age, create a new error value specifying
// an error message and the age value for which validation failed.
return error("Age cannot be negative", age = age);
}
}

public function main() {
Person[] people = [
{name: "Alice", age: 25},
{name: "Bob", age: -1}
{name: "Bob", age: -1},
{name: "Charlie", age: 30}
];
error? err = validatePeople(people);
if err != () {
if err is error {
printError(err);
}
}

// Helper function to print internals of error value.
function printError(error err, int depth = 0) {
string indent = "".join(...from int _ in 0 ..< depth
select " ");
io:println(indent + "message: ", err.message());
io:println(indent + "details: ", err.detail());
io:println(indent + "stack trace: ", err.stackTrace());
function printError(error err) {
io:println("Message: ", err.message());
io:println("Detail: ", err.detail());
io:println("Stack trace: ", err.stackTrace());

error? cause = err.cause();
if cause != () {
io:println(indent + "cause: ");
printError(cause, depth + 1);
if cause is error {
io:println("Cause:");
printError(cause);
}
}
13 changes: 7 additions & 6 deletions examples/error-reporting/error_reporting.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# Errors
In Ballerina invalid states are represented by `error` values. Each error value has,
1. Message, a human-readable `string` describing the error.
2. Cause, which is an `error` value if this error was caused by another error, otherwise nil.
3. Detail, a mapping value which can be used to provide additional information about the error.

Ballerina does not have exceptions. Instead functions report invalid states by returning error values. Each error value has,
1. Message, a human-readable `string` value describing the error.
2. Cause, which is an `error` value if this error was caused due to another error, which needs to be propagated, otherwise nil.
3. Detail, a mapping value consisting of additional information about the error.
4. Stack trace, a snapshot of the state of the execution stack when the error value was created.

Error values are immutable.
Error values are immutable.

You can create new error values by calling the error constructor. As the first argument to the error constructor, it expects the `message` string. As the second argument, you can optionally pass in an `error?` value for cause. The remaining named arguments will be used to create the detail record. The stack trace is provided by the runtime.
You can create a new error value using an error constructor. As the first argument to the error constructor, it expects the message string. As the second argument, you can optionally pass in an `error?` value for cause. Subsequent named arguments, if specified, will be used to create the detail mapping. The stack trace is provided by the runtime.

::: code error_reporting.bal :::

Expand Down
16 changes: 9 additions & 7 deletions examples/error-reporting/error_reporting.out
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
$ bal run error_reporting.bal
message: failed to validate people
details: {"people":[{"name":"Alice","age":25},{"name":"Bob","age":-1}]}
stack trace: [callableName: validatePeople fileName: error_reporting.bal lineNumber: 18,callableName: main fileName: error_reporting.bal lineNumber: 35]
cause:
message: age cannot be negative
details: {"person":{"name":"Bob","age":-1}}
stack trace: [callableName: validatePerson fileName: error_reporting.bal lineNumber: 26,callableName: validatePeople fileName: error_reporting.bal lineNumber: 15,callableName: main fileName: error_reporting.bal lineNumber: 35]
Validating Alice
Validating Bob
Message: Validation failed for a person
Detail: {"person":{"name":"Bob","age":-1}}
Stack trace: [callableName: validatePeople fileName: error_reporting.bal lineNumber: 20,callableName: main fileName: error_reporting.bal lineNumber: 40]
Cause:
Message: Age cannot be negative
Detail: {"age":-1}
Stack trace: [callableName: validatePerson fileName: error_reporting.bal lineNumber: 30,callableName: validatePeople fileName: error_reporting.bal lineNumber: 16,callableName: main fileName: error_reporting.bal lineNumber: 40]
11 changes: 5 additions & 6 deletions examples/error-subtyping/error_subtyping.bal
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ type InvalidI32Detail record {|
int:Signed32 value;
|};

// `error` with `InvalidIntDetail` as the detail type.
// Error with the `InvalidIntDetail` type as the detail type.
type InvalidIntError error<InvalidIntDetail>;

// `error` with `InvalidI32Detail` as the detail type. Thus it is a subtype of `InvalidIntError`.
type InvalidI32Error error<InvalidI32Detail>;

// `error` with `InvalidIntDetail` as the detail type and a unique type ID.
// Therefore this is a proper subtype of `InvalidIntError`, but don't have a subtype relationship
// Distinct error with the `InvalidIntDetail` type as the detail type and a unique type ID.
// Therefore, this is a proper subtype of `InvalidIntError`, but doesn't have a subtype relationship
// with `AnotherDistinctIntError` because they have different type IDs.
type DistinctIntError distinct error<InvalidIntDetail>;

// Another `error` with `InvalidIntDetail` as the detail type and different type ID to `DistinctIntError`
// This is also a proper subtype of `InvalidIntError`, but don't have a subtype relationship with `DistinctIntError`
// This is also a proper subtype of `InvalidIntError`, but doesn't have a subtype relationship with `DistinctIntError`
type AnotherDistinctIntError distinct error<InvalidIntDetail>;

public function main() {
Expand All @@ -39,7 +39,7 @@ public function main() {
io:println(e3 is AnotherDistinctIntError);
}

// Helper functions to create errors
// Helper functions to create errors.
function createInvalidIntError(int value) returns InvalidIntError {
return error("Invalid int", value = value);
}
Expand All @@ -51,4 +51,3 @@ function createDistinctInvalidIntError(int value) returns DistinctIntError {
function createInvalidI32Error(int:Signed32 value) returns InvalidI32Error {
return error("Invalid i32", value = value);
}

6 changes: 4 additions & 2 deletions examples/error-subtyping/error_subtyping.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Error subtyping

If we want to check if a given `error` type (say `e1`) is a supertype of another `error` type (say `e2`), first we need to check if `e1` is a distinct error type. If it's not the case then `e1` is a supertype of `e2` if and only if the detail record type of `e1` is a supertype of the detail record type of `e2`.
If we want to identify if a given `error` type (say `ESub`) is a subtype of another error type (say `ESuper`), first we need to check if `ESuper` is a distinct error type. If it is not then `ESub` is a subtype if and only if the detail type of `ESub` is a subtype of the detail type of `ESuper`.

If more explicit control over error type relations is desired you can use `distinct` error types. Each declaration of a distinct error type has a unique type ID. If the supertype is a `distinct` error type then there is the additional requirement that it must contain all the type IDs of the subtype. Note that you can create subtypes of distinct error types by intersecting them with other error types.
If more explicit control over error type relations is desired you can use `distinct` error types. Each declaration of a distinct error type has a unique type ID. If `ESuper` is a distinct error type there is the additional requirement that type ID of `ESub` must belong to the type ID set of `ESuper` for it to be a subtype.

Note that you can create subtypes of distinct error types by intersecting them with other error types.

```ballerina
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function createDistinctNumericInputError(int value) returns DistinctNumericInput

public function main() {
NumericInputError e1 = createNumericInputError(5);
// `e1` belong to `InputError` since it's detail type is a subtype of `InputErrorDetail`.
// `e1` belong to `InputError` since its detail type is a subtype of `InputErrorDetail`.
io:println(e1 is InputError);

// `e1` doesn't belong to `DistinctInputError` since it doesn't have the type id of `DistinctInputError`.
Expand Down
9 changes: 6 additions & 3 deletions examples/trap-expression/trap_expression.bal
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,23 @@ function hereBeDragons() returns int {
alwaysPanic();
}

// Return type `never` indicate that this function will never return normally. That is it will always panic
// Return type `never` indicate that this function will never return normally. I.e., it will always panic.
function alwaysPanic() returns never {
panic error("deep down in the code");
}

public function main() {
// Division by 0 trigger a panic
// Division by 0 triggers a panic.
int|error result = trap 1 / 0;
if result is error {
io:println("Error: ", result);
}
// This will tigger a panic deep within the call stack and we'll trap it here
// Calling the `hereBeDragons` function triggers a panic deep within the call stack, which is trapped here.
int|error result2 = trap hereBeDragons();
if result2 is error {
io:println("Error: ", result2);
}
// This will trigger a panic which is not trapped. Thus it will terminate the program.
int result3 = hereBeDragons();
io:println("Result: ", result3);
}
2 changes: 1 addition & 1 deletion examples/trap-expression/trap_expression.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Trap expression

If you have an expression such as a function call that can potentially trigger a `panic` you can use a `trap` expression to prevent further unwinding of the stack. Then if the evaluation of the expression trigger a panic you will get the `error` associated with the panic. Otherwise you will get the result of the expression.
If you have an expression such as a function call that can potentially trigger a panic you can use a `trap` expression to prevent further unwinding of the stack. Then if the evaluation of the expression triggers a panic you will get the `error` value associated with the panic. Otherwise, you will get the result of the expression.

::: code trap_expression.bal :::

Expand Down
5 changes: 4 additions & 1 deletion examples/trap-expression/trap_expression.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
$ bal run trap_expression.bal
Error: error("{ballerina}DivisionByZero",message=" / by zero")
Error: error("deep down in the code")

error: deep down in the code
at trap_expression:alwaysPanic(trap_expression.bal:10)
trap_expression:hereBeDragons(trap_expression.bal:5)
trap_expression:main(trap_expression.bal:25)

0 comments on commit 686a4ae

Please sign in to comment.