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 <[email protected]>
  • Loading branch information
heshanpadmasiri and MaryamZi committed Oct 9, 2024
1 parent 9db9dcf commit 76d3d05
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 89 deletions.
14 changes: 7 additions & 7 deletions examples/check-expression/check_expression.bal
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import ballerina/io;

function intFromBytesWithCheck(byte[] bytes) returns int|error {
string str = check string:fromBytes(bytes);
return int:fromString(str);
}

// Same as `intFromBytesWithCheck` but with explicit error handling.
function intFromBytesExplicit(byte[] bytes) returns int|error {
function intFromBytes(byte[] bytes) returns int|error {
string|error res = string:fromBytes(bytes);
// Handling the error explicitly.
if res is error {
Expand All @@ -15,9 +10,14 @@ function intFromBytesExplicit(byte[] bytes) returns int|error {
return int:fromString(res);
}

function intFromBytesWithCheck(byte[] bytes) returns int|error {
string str = check string:fromBytes(bytes);
return int:fromString(str);
}

public function main() {
int|error res1 = intFromBytesWithCheck([104, 101, 108, 108, 111]);
io:println(res1);
int|error res2 = intFromBytesExplicit([104, 101, 108, 108, 111]);
int|error res2 = intFromBytes([104, 101, 108, 108, 111]);
io:println(res2);
}
4 changes: 3 additions & 1 deletion examples/check-expression/check_expression.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Check expression

In Ballerina, it is common to write an expression that may result in an error, such as calling a function that could return an error value, checking if the result belongs to the `error` type and immediately returning that value. You can use the `check` expression to simplify this pattern.
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.

::: code check_expression.bal :::

::: out check_expression.out :::

+ [`check` semantics](https://ballerina.io/learn/concurrency/#check-semantics)
1 change: 1 addition & 0 deletions examples/check-expression/check_expression.out
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
$ 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'")

83 changes: 28 additions & 55 deletions examples/error-reporting/error_reporting.bal
Original file line number Diff line number Diff line change
@@ -1,67 +1,40 @@
import ballerina/io;

function parseInt(string s) returns int|error {
if s.length() == 0 {
return error("empty string"); // Create error with only a message.
type Person record {
string name;
int age;
};

function validatePeople(Person[] people) returns error? {
if people.length() == 0 {
// Create error with only a message.
return error("empty people array");
}
int pow = 0;
int val = 0;
foreach string:Char char in s {
int|error digit = parseDigit(char);
if digit is error {
// Create a new error value that include the error from parsing as
// the cause.
return error("failed to parse digit", digit, stringValue = s);

foreach Person p in people {
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);
}
val += val * 10 + digit;
pow += 1;
}
return val;
}

function parseDigit(string:Char s) returns int|error {
match s {
"1" => {
return 1;
}
"2" => {
return 2;
}
"3" => {
return 3;
}
"4" => {
return 4;
}
"5" => {
return 5;
}
"6" => {
return 6;
}
"7" => {
return 7;
}
"8" => {
return 8;
}
"9" => {
return 9;
}
"0" => {
return 0;
}
_ => {
// Create an error value with field `charValue` in it's detail.
return error("unexpected char for digit value", charValue = s);
}
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);
}
}

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

Expand All @@ -70,11 +43,11 @@ 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 + "details: ", err.detail());
io:println(indent + "stack trace: ", err.stackTrace());
error? cause = err.cause();
if cause != () {
io:println(indent + "cause: ", cause);
io:println(indent + "cause: ");
printError(cause, depth + 1);
}
}
2 changes: 1 addition & 1 deletion examples/error-reporting/error_reporting.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# 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`.
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.
4. Stack trace, a snapshot of the state of the execution stack when the error value was created.

Expand Down
14 changes: 7 additions & 7 deletions examples/error-reporting/error_reporting.out
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
$ bal run error_reporting.bal
message: failed to parse digit
details {"stringValue":"1x3"}
stack trace: [callableName: parseInt fileName: error_reporting.bal lineNumber: 14,callableName: main fileName: error_reporting.bal lineNumber: 62]
cause: error("unexpected char for digit value",charValue="x")
message: unexpected char for digit value
details {"charValue":"x"}
stack trace: [callableName: parseDigit fileName: error_reporting.bal lineNumber: 56,callableName: parseInt fileName: error_reporting.bal lineNumber: 10,callableName: main fileName: error_reporting.bal lineNumber: 62]
message: failed to validate people
details: {"people":[{"name":"Alice","age":25},{"name":"Bob","age":-1}]}
stack trace: [callableName: validatePeople fileName: error_reporting.bal lineNumber: 12,callableName: main fileName: error_reporting.bal lineNumber: 28]
cause:
message: age cannot be negative
details: {"person":{"name":"Bob","age":-1}}
stack trace: [callableName: validatePerson fileName: error_reporting.bal lineNumber: 19,callableName: validatePeople fileName: error_reporting.bal lineNumber: 10,callableName: main fileName: error_reporting.bal lineNumber: 28]
36 changes: 22 additions & 14 deletions examples/error-subtyping/error_subtyping.bal
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,31 @@ type InvalidIntDetail record {|
int value;
|};

// Subtype of `InvalidIntDetail`.
type InvalidI32Detail record {|
int:Signed32 value;
|};

// `error` with `InvalidIntDetail` 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
// 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`
type AnotherDistinctIntError distinct error<InvalidIntDetail>;

function createInvalidIntError(int value) returns InvalidIntError {
return error("Invalid int", value = value);
}

function createDistinctInvalidIntError(int value) returns DistinctIntError {
return error("Invalid int", value = value);
}

function createInvalidI32Error(int:Signed32 value) returns InvalidI32Error {
return error("Invalid i32", value = value);
}

public function main() {
InvalidI32Error e1 = createInvalidI32Error(5);
// This is true because `InvalidI32Detail` is a subtype of `InvalidIntDetail`.
io:println(e1 is InvalidIntError);

InvalidIntError e2 = createInvalidIntError(5);
// This is false because `e2` don't have the type id corresponding to `DistinctIntError`.
io:println(e2 is DistinctIntError);

DistinctIntError e3 = createDistinctInvalidIntError(5);
Expand All @@ -44,3 +38,17 @@ public function main() {
// This is false because `DistinctIntError` and `AnotherDistinctIntError` have different type ids.
io:println(e3 is AnotherDistinctIntError);
}

// Helper functions to create errors
function createInvalidIntError(int value) returns InvalidIntError {
return error("Invalid int", value = value);
}

function createDistinctInvalidIntError(int value) returns DistinctIntError {
return error("Invalid int", value = value);
}

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

A given non-distinct `error` type (such as `InvalidIntDetail` in the example) is a supertype of another error type (say `InvalidI32Error`) if and only if the latter's detail record type (`InvalidIntDetail`) is super type of the former's detail type (`InvalidI32Detail`).
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 more explicit control over the error type relations is desired you can use `distinct` error types. Each declaration of a distinct error type has a unique type ID. For instance, the `DistinctIntError` and `AnotherDistinctIntError` have different type IDs. 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.
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.

```ballerina
::: code error_subtyping.bal :::
Expand Down
4 changes: 3 additions & 1 deletion examples/error-type-intersection/error_type_intersection.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Type intersection for error types

If you intersect two `error` types, the resulting type's detail type is the intersection of the detail types of both types. Furthermore, if any of the types being intersected is a distinct type, then the resultant type's type ID set includes all the type IDs of that type.
If you intersect two `error` types, the resulting type's detail type is the intersection of the detail types of both types. Furthermore, if any of the types being intersected is a distinct type, then the resultant type's type ID set includes all the type IDs of that type. Thus it is a subtype of both types and this is how you create subtypes of `distinct` error types.

::: code error_type_intersection.bal :::

::: out error_type_intersection.out :::

+ [Error subtyping](https://ballerina.io/learn/by-example/error-subtyping/)
2 changes: 1 addition & 1 deletion examples/panics/panics.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Panics

Ballerina distinguishes normal errors from abnormal errors. Normal errors are handled by returning error values. This signals to the caller that they must handle the error. In contrast, abnormal errors such as division by 0 and out-of-memory are typically unrecoverable errors and we need to terminate the execution of the program. This is achieved by a panic statement, which expects an expression that results in an error value such as the error constructor. At runtime evaluating a panic statement first create the error value by evaluating the expression and then start stack unwinding. In this process, if it comes across a trap expression it stops further unwinding and as a result of that trap expression, you will get the error created at the panic statement.
Ballerina distinguishes normal errors from abnormal errors. Normal errors are handled by returning error values. This signals to the caller that they must handle the error. In contrast, abnormal errors such as division by 0 and out-of-memory are typically unrecoverable errors and we need to terminate the execution of the program. This is achieved by a panic statement with an expression that results in an error value such as the error constructor. At runtime, evaluating a panic statement first creates the error value by evaluating the expression and then starts stack unwinding, unless it encounters a `trap` expression.

::: code panics.bal :::

Expand Down

0 comments on commit 76d3d05

Please sign in to comment.