From 036758ba778fa9f0f178346655709006080e1ce0 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 7 Oct 2024 11:55:27 +0530 Subject: [PATCH 1/8] Update error_reporting --- examples/error-reporting/error_reporting.bal | 91 +++++++++++++++----- examples/error-reporting/error_reporting.md | 17 +++- examples/error-reporting/error_reporting.out | 9 +- 3 files changed, 90 insertions(+), 27 deletions(-) diff --git a/examples/error-reporting/error_reporting.bal b/examples/error-reporting/error_reporting.bal index 7b0b31ec52..6f5965db47 100644 --- a/examples/error-reporting/error_reporting.bal +++ b/examples/error-reporting/error_reporting.bal @@ -1,31 +1,80 @@ import ballerina/io; -// Parses a `string` value to convert to an `int` value. This function may return error values. -// The return type is a union with `error`. -function parse(string s) returns int|error { - - int n = 0; - int[] cps = s.toCodePointInts(); - foreach int cp in cps { - int p = cp - 0x30; - if p < 0 || p > 9 { - // If `p` is not a digit construct, it returns an `error` value with `not a digit` as the error message. - return error("not a digit"); +function parseInt(string s) returns int|error { + if s.length() == 0 { + return error("empty string"); // Create error with only a message. + } + 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); + } + 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); } - n = n * 10 + p; } - return n; } public function main() { - // An `int` value is returned when the argument is a `string` value, which can be parsed as an integer. - int|error x = parse("123"); - - io:println(x); - - // An `error` value is returned when the argument is a `string` value, which has a character that is not a digit. - int|error y = parse("1h"); + int|error result = parseInt("1x3"); + if result is error { + printError(result); + } +} - io:println(y); +// 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()); + error? cause = err.cause(); + if cause != () { + io:println(indent + "cause: ", cause); + printError(cause, depth + 1); + } } diff --git a/examples/error-reporting/error_reporting.md b/examples/error-reporting/error_reporting.md index 2edbcb6b8a..0bb6eb361b 100644 --- a/examples/error-reporting/error_reporting.md +++ b/examples/error-reporting/error_reporting.md @@ -1,10 +1,19 @@ # 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. +4. Stack trace, a snapshot of the state of the execution stack when the error value was created. -Ballerina does not have exceptions. Errors are reported by functions returning `error` values. -`error` is its own basic type. The return type of a function that may return an `error` value will be a union with `error`. +Error values are immutable. -An `error` value includes a `string` message. An `error` value includes the stack trace from the point at which the error is constructed (i.e., `error(msg)` is called). 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. ::: code error_reporting.bal ::: -::: out error_reporting.out ::: \ No newline at end of file +::: out error_reporting.out ::: + +## Related links +- [Error subtyping](https://ballerina.io/learn/by-example/error-subtyping/) +- [Error cause](https://ballerina.io/learn/by-example/error-cause/) +- [Error detail](https://ballerina.io/learn/by-example/error-detail/) diff --git a/examples/error-reporting/error_reporting.out b/examples/error-reporting/error_reporting.out index 690a047e44..57e781d4a0 100644 --- a/examples/error-reporting/error_reporting.out +++ b/examples/error-reporting/error_reporting.out @@ -1,3 +1,8 @@ $ bal run error_reporting.bal -123 -error("not a digit") +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] From f389761de460f6ee59ab8bf6675944fd7836801c Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 7 Oct 2024 13:34:48 +0530 Subject: [PATCH 2/8] Update error subtype --- examples/error-subtyping/error_subtyping.bal | 47 +++++++++++++++----- examples/error-subtyping/error_subtyping.md | 9 ++-- examples/error-subtyping/error_subtyping.out | 5 ++- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/examples/error-subtyping/error_subtyping.bal b/examples/error-subtyping/error_subtyping.bal index e11053e0dc..7d813b3b0f 100644 --- a/examples/error-subtyping/error_subtyping.bal +++ b/examples/error-subtyping/error_subtyping.bal @@ -1,21 +1,46 @@ import ballerina/io; -// `distinct` creates a new subtype. -type XErr distinct error; -type YErr distinct error; +type InvalidIntDetail record {| + int value; +|}; -type Err XErr|YErr; +type InvalidI32Detail record {| + int:Signed32 value; +|}; -// The name of the distinct type can be used with the `error` constructor to create an error value -// of that type. `err` holds an `error` value of type `XErr`. -Err err = error XErr("Whoops!"); +type InvalidIntError error; -function desc(Err err) returns string { - // The `is` operator can be used to distinguish distinct subtypes. - return err is XErr ? "X" : "Y"; +type InvalidI32Error error; +type DistinctIntError distinct error; + +type AnotherDistinctIntError distinct error; + +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() { - io:println(desc(err)); + 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); + // This is true because `InvalidInt` is not a distinct type, thus it ignores the type id of `e3`. + io:println(e3 is InvalidIntError); + + // This is false because `DistinctIntError` and `AnotherDistinctIntError` have different type ids. + io:println(e3 is AnotherDistinctIntError); } diff --git a/examples/error-subtyping/error_subtyping.md b/examples/error-subtyping/error_subtyping.md index f2d69e7966..73df49d002 100644 --- a/examples/error-subtyping/error_subtyping.md +++ b/examples/error-subtyping/error_subtyping.md @@ -1,9 +1,12 @@ # Error subtyping -`distinct` creates a new subtype and can be used to define subtypes of `error`. The name of the distinct `error` type can be used with the error constructor to create an `error` value of that type. +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`). -Works like a nominal type. The `is` operator can be used to distinguish distinct subtypes. Each occurrence of `distinct` has a unique identifier that is used to tag instances of the type. +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. ::: code error_subtyping.bal ::: -::: out error_subtyping.out ::: \ No newline at end of file +::: out error_subtyping.out ::: + +## Related links +- [Type intersection for error types](https://ballerina.io/learn/by-example/error-type-intersection/) diff --git a/examples/error-subtyping/error_subtyping.out b/examples/error-subtyping/error_subtyping.out index d0ed13cfaa..7f8675bb19 100644 --- a/examples/error-subtyping/error_subtyping.out +++ b/examples/error-subtyping/error_subtyping.out @@ -1,2 +1,5 @@ $ bal run error_subtyping.bal -X +true +false +true +false From 32c9132ed04345474b159a9fb1a5211f38a2a30b Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 7 Oct 2024 14:13:28 +0530 Subject: [PATCH 3/8] Update error intersection --- .../error_type_intersection.bal | 66 +++++++++++-------- .../error_type_intersection.md | 2 +- .../error_type_intersection.out | 4 +- 3 files changed, 43 insertions(+), 29 deletions(-) diff --git a/examples/error-type-intersection/error_type_intersection.bal b/examples/error-type-intersection/error_type_intersection.bal index cd904bcb04..832dc4820c 100644 --- a/examples/error-type-intersection/error_type_intersection.bal +++ b/examples/error-type-intersection/error_type_intersection.bal @@ -1,33 +1,47 @@ import ballerina/io; -type IOError distinct error; +type InputErrorDetail record {| + int|string value; +|}; -type FileErrorDetail record { - string filename; -}; +type NumericErrorDetail record {| + int|float value; +|}; -// The `FileIOError` type is defined as an intersection type using the `&` notation. -// It is the intersection of two error types: `IOError` and `error`. -// An error value belongs to this type if and only if it belongs to both `IOError` -// and `error`. -type FileIOError IOError & error; +type InputError error; + +type NumericError error; + +type DistinctInputError distinct error; + +type DistinctNumericError distinct error; + +// `NumericInputError` has detail type, `record {| int value |}`. +type NumericInputError InputError & NumericError; + +// `DistinctNumericInputError` has type ids of both `DistinctInputError` and `DistinctNumericError`. +type DistinctNumericInputError DistinctInputError & DistinctNumericError; + +function createNumericInputError(int value) returns NumericInputError { + return error("Numeric input error", value = value); +} + +function createDistinctNumericInputError(int value) returns DistinctNumericInputError { + return error("Distinct numeric input error", value = value); +} public function main() { - // In order to create an error value that belongs to `FileIOError`, the `filename` - // detail field must be provided. - FileIOError fileIOError = error("file not found", filename = "test.txt"); - - // `fileIOError` belongs to both `IOError` and `error`. - io:println(fileIOError is IOError); - io:println(fileIOError is error); - - // An `IOError` value will not belong to `FileIOError` if it doesn't belong to - // `error`. - IOError ioError = error("invalid input"); - io:println(ioError is FileIOError); - - // Similarly, an error value belonging to `error` will not belong - // to `FileIOError` if it doesn't belong to `IOError`. - error fileError = error("cannot remove file", filename = "test.txt"); - io:println(fileError is FileIOError); + NumericInputError e1 = createNumericInputError(5); + // `e1` belong to `InputError` since it's 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`. + io:println(e1 is DistinctInputError); + + DistinctNumericInputError e2 = createDistinctNumericInputError(5); + // `e2` belong to `InputError` since it's detail type is a subtype of `InputErrorDetail`. + io:println(e2 is InputError); + + // `e2` belong to `DistinctInputError` since it's type id set include the type id of `DistinctInputError`. + io:println(e2 is DistinctInputError); } diff --git a/examples/error-type-intersection/error_type_intersection.md b/examples/error-type-intersection/error_type_intersection.md index 2b65eb1bb1..8493c9da00 100644 --- a/examples/error-type-intersection/error_type_intersection.md +++ b/examples/error-type-intersection/error_type_intersection.md @@ -1,6 +1,6 @@ # Type intersection for error types -You can define an error type that is both a subtype of a `distinct` error type and has additional constraints on the detail fields using intersection 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. ::: code error_type_intersection.bal ::: diff --git a/examples/error-type-intersection/error_type_intersection.out b/examples/error-type-intersection/error_type_intersection.out index e315509605..20c659f272 100644 --- a/examples/error-type-intersection/error_type_intersection.out +++ b/examples/error-type-intersection/error_type_intersection.out @@ -1,5 +1,5 @@ $ bal run error_type_intersection.bal true -true -false false +true +true From dd98929d5dd10e369358ef618f26952b869c0f80 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 7 Oct 2024 16:08:04 +0530 Subject: [PATCH 4/8] Update panic --- examples/panics/panics.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/panics/panics.md b/examples/panics/panics.md index 932fb94a60..74f126d51e 100644 --- a/examples/panics/panics.md +++ b/examples/panics/panics.md @@ -1,9 +1,7 @@ # Panics -Ballerina distinguishes normal errors from abnormal errors. Normal errors are handled by returning error values. Abnormal errors are handled using the panic statement. Abnormal errors should typically result in immediate program termination. - -E.g., A programming bug or out of memory. A panic has an associated error value. +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. ::: code panics.bal ::: -::: out panics.out ::: \ No newline at end of file +::: out panics.out ::: From 2e095e789cb6fe385226b47a6213b902192a3eeb Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 8 Oct 2024 08:04:34 +0530 Subject: [PATCH 5/8] Update check expression --- .../check-expression/check_expression.bal | 25 +++++++++++-------- examples/check-expression/check_expression.md | 6 ++--- .../check-expression/check_expression.out | 1 + 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/examples/check-expression/check_expression.bal b/examples/check-expression/check_expression.bal index 702677244d..1f62483012 100644 --- a/examples/check-expression/check_expression.bal +++ b/examples/check-expression/check_expression.bal @@ -1,18 +1,23 @@ import ballerina/io; -// Convert `bytes` to a `string` value and then to an `int` value. -function intFromBytes(byte[] bytes) returns int|error { - - // Use `check` with an expression that may return `error`. - // If `string:fromBytes(bytes)` returns an `error` value, `check` - // makes the function return the `error` value here. - // If not, the returned `string` value is used as the value of the `str` variable. +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 { + string|error res = string:fromBytes(bytes); + // Handling the error explicitly. + if res is error { + return res; + } + return int:fromString(res); +} + public function main() { - int|error res = intFromBytes([104, 101, 108, 108, 111]); - io:println(res); + int|error res1 = intFromBytesWithCheck([104, 101, 108, 108, 111]); + io:println(res1); + int|error res2 = intFromBytesExplicit([104, 101, 108, 108, 111]); + io:println(res2); } diff --git a/examples/check-expression/check_expression.md b/examples/check-expression/check_expression.md index 0e5ff11344..78cfe842c8 100644 --- a/examples/check-expression/check_expression.md +++ b/examples/check-expression/check_expression.md @@ -1,9 +1,7 @@ # Check expression -`check E` is used with an expression `E` that might result in an `error` value. If `E` results in an `error` value , then, `check` makes the function return that `error` value immediately. - -The type of `check E` does not include `error`. The control flow remains explicit. +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. ::: code check_expression.bal ::: -::: out check_expression.out ::: \ No newline at end of file +::: out check_expression.out ::: diff --git a/examples/check-expression/check_expression.out b/examples/check-expression/check_expression.out index aed09b016e..b763450360 100644 --- a/examples/check-expression/check_expression.out +++ b/examples/check-expression/check_expression.out @@ -1,2 +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'") From b371584e390d26fd565158d0d6a6dc7085382d0c Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 9 Oct 2024 10:08:25 +0530 Subject: [PATCH 6/8] Apply suggestions from code review Co-authored-by: Maryam Ziyad --- .../check-expression/check_expression.bal | 14 ++-- examples/check-expression/check_expression.md | 4 +- .../check-expression/check_expression.out | 1 + examples/error-reporting/error_reporting.bal | 83 +++++++------------ examples/error-reporting/error_reporting.md | 2 +- examples/error-reporting/error_reporting.out | 14 ++-- examples/error-subtyping/error_subtyping.bal | 36 ++++---- examples/error-subtyping/error_subtyping.md | 6 +- .../error_type_intersection.md | 4 +- examples/panics/panics.md | 2 +- 10 files changed, 77 insertions(+), 89 deletions(-) diff --git a/examples/check-expression/check_expression.bal b/examples/check-expression/check_expression.bal index 1f62483012..4dfadf7625 100644 --- a/examples/check-expression/check_expression.bal +++ b/examples/check-expression/check_expression.bal @@ -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 { @@ -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); } diff --git a/examples/check-expression/check_expression.md b/examples/check-expression/check_expression.md index 78cfe842c8..52ae7350c8 100644 --- a/examples/check-expression/check_expression.md +++ b/examples/check-expression/check_expression.md @@ -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) diff --git a/examples/check-expression/check_expression.out b/examples/check-expression/check_expression.out index b763450360..ceac7007f2 100644 --- a/examples/check-expression/check_expression.out +++ b/examples/check-expression/check_expression.out @@ -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'") + diff --git a/examples/error-reporting/error_reporting.bal b/examples/error-reporting/error_reporting.bal index 6f5965db47..5d9bc49dca 100644 --- a/examples/error-reporting/error_reporting.bal +++ b/examples/error-reporting/error_reporting.bal @@ -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); } } @@ -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); } } diff --git a/examples/error-reporting/error_reporting.md b/examples/error-reporting/error_reporting.md index 0bb6eb361b..386a6c08e6 100644 --- a/examples/error-reporting/error_reporting.md +++ b/examples/error-reporting/error_reporting.md @@ -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. diff --git a/examples/error-reporting/error_reporting.out b/examples/error-reporting/error_reporting.out index 57e781d4a0..5fe7b170a3 100644 --- a/examples/error-reporting/error_reporting.out +++ b/examples/error-reporting/error_reporting.out @@ -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: 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] diff --git a/examples/error-subtyping/error_subtyping.bal b/examples/error-subtyping/error_subtyping.bal index 7d813b3b0f..d1d3a1e89e 100644 --- a/examples/error-subtyping/error_subtyping.bal +++ b/examples/error-subtyping/error_subtyping.bal @@ -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; +// `error` with `InvalidI32Detail` as the detail type. Thus it is a subtype of `InvalidIntError`. type InvalidI32Error error; +// `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; +// 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; -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); @@ -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); +} + diff --git a/examples/error-subtyping/error_subtyping.md b/examples/error-subtyping/error_subtyping.md index 73df49d002..74112f9e95 100644 --- a/examples/error-subtyping/error_subtyping.md +++ b/examples/error-subtyping/error_subtyping.md @@ -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 ::: diff --git a/examples/error-type-intersection/error_type_intersection.md b/examples/error-type-intersection/error_type_intersection.md index 8493c9da00..24b8631453 100644 --- a/examples/error-type-intersection/error_type_intersection.md +++ b/examples/error-type-intersection/error_type_intersection.md @@ -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/) diff --git a/examples/panics/panics.md b/examples/panics/panics.md index 74f126d51e..4dcee684b1 100644 --- a/examples/panics/panics.md +++ b/examples/panics/panics.md @@ -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 ::: From ec36a2e0a0aec3262a9a9bc78fecb6b588d64748 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 9 Oct 2024 10:37:48 +0530 Subject: [PATCH 7/8] Add panic BBE --- examples/index.json | 7 ++++++ examples/trap-expression/trap_expression.bal | 24 +++++++++++++++++++ examples/trap-expression/trap_expression.md | 9 +++++++ .../trap-expression/trap_expression.metatags | 2 ++ examples/trap-expression/trap_expression.out | 4 ++++ 5 files changed, 46 insertions(+) create mode 100644 examples/trap-expression/trap_expression.bal create mode 100644 examples/trap-expression/trap_expression.md create mode 100644 examples/trap-expression/trap_expression.metatags create mode 100644 examples/trap-expression/trap_expression.out diff --git a/examples/index.json b/examples/index.json index 7fe1827931..8a3f0e9932 100644 --- a/examples/index.json +++ b/examples/index.json @@ -1369,6 +1369,13 @@ "verifyOutput": true, "isLearnByExample": true }, + { + "name": "Trap expression", + "url": "trap-expression", + "verifyBuild": true, + "verifyOutput": true, + "isLearnByExample": true + }, { "name": "Type intersection for error types", "url": "error-type-intersection", diff --git a/examples/trap-expression/trap_expression.bal b/examples/trap-expression/trap_expression.bal new file mode 100644 index 0000000000..d9736e0edb --- /dev/null +++ b/examples/trap-expression/trap_expression.bal @@ -0,0 +1,24 @@ +import ballerina/io; + +function hereBeDragons() returns int { + // Trigger a panic deep within the call stack. + alwaysPanic(); +} + +// Return type `never` indicate that this function will never return normally. That is it will always panic +function alwaysPanic() returns never { + panic error("deep down in the code"); +} + +public function main() { + // Division by 0 trigger 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 + int|error result2 = trap hereBeDragons(); + if result2 is error { + io:println("Error: ", result2); + } +} diff --git a/examples/trap-expression/trap_expression.md b/examples/trap-expression/trap_expression.md new file mode 100644 index 0000000000..6ec7ca0adf --- /dev/null +++ b/examples/trap-expression/trap_expression.md @@ -0,0 +1,9 @@ +# 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. + +::: code trap_expression.bal ::: + +::: out trap_expression.out ::: + ++ [Panics](https://ballerina.io/learn/by-example/panics/) diff --git a/examples/trap-expression/trap_expression.metatags b/examples/trap-expression/trap_expression.metatags new file mode 100644 index 0000000000..da44b3deb8 --- /dev/null +++ b/examples/trap-expression/trap_expression.metatags @@ -0,0 +1,2 @@ +description: This BBE demonstrates how the trap expression is used in Ballerina to handle panics +keywords: ballerina, ballerina by example, bbe, error, panic, trap diff --git a/examples/trap-expression/trap_expression.out b/examples/trap-expression/trap_expression.out new file mode 100644 index 0000000000..d8e5f96579 --- /dev/null +++ b/examples/trap-expression/trap_expression.out @@ -0,0 +1,4 @@ +$ bal run trap_expression.bal +Error: error("{ballerina}DivisionByZero",message=" / by zero") +Error: error("deep down in the code") + From 77315ea6d3db978b452535a8ef047a15cfa12af3 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 14 Oct 2024 09:16:52 +0530 Subject: [PATCH 8/8] Apply suggestions from code review Co-authored-by: Maryam Ziyad --- .../check-expression/check_expression.bal | 5 ++- examples/check-expression/check_expression.md | 2 +- .../check-expression/check_expression.out | 1 - examples/error-reporting/error_reporting.bal | 42 ++++++++++--------- examples/error-reporting/error_reporting.md | 13 +++--- examples/error-reporting/error_reporting.out | 16 +++---- examples/error-subtyping/error_subtyping.bal | 11 +++-- examples/error-subtyping/error_subtyping.md | 6 ++- .../error_type_intersection.bal | 2 +- examples/trap-expression/trap_expression.bal | 9 ++-- examples/trap-expression/trap_expression.md | 2 +- examples/trap-expression/trap_expression.out | 5 ++- 12 files changed, 64 insertions(+), 50 deletions(-) diff --git a/examples/check-expression/check_expression.bal b/examples/check-expression/check_expression.bal index 4dfadf7625..6d68881fa4 100644 --- a/examples/check-expression/check_expression.bal +++ b/examples/check-expression/check_expression.bal @@ -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); diff --git a/examples/check-expression/check_expression.md b/examples/check-expression/check_expression.md index 52ae7350c8..df47a34c14 100644 --- a/examples/check-expression/check_expression.md +++ b/examples/check-expression/check_expression.md @@ -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 ::: diff --git a/examples/check-expression/check_expression.out b/examples/check-expression/check_expression.out index ceac7007f2..b763450360 100644 --- a/examples/check-expression/check_expression.out +++ b/examples/check-expression/check_expression.out @@ -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'") - diff --git a/examples/error-reporting/error_reporting.bal b/examples/error-reporting/error_reporting.bal index 5d9bc49dca..e85a2ff841 100644 --- a/examples/error-reporting/error_reporting.bal +++ b/examples/error-reporting/error_reporting.bal @@ -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); } } diff --git a/examples/error-reporting/error_reporting.md b/examples/error-reporting/error_reporting.md index 386a6c08e6..8a4faeb247 100644 --- a/examples/error-reporting/error_reporting.md +++ b/examples/error-reporting/error_reporting.md @@ -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 ::: diff --git a/examples/error-reporting/error_reporting.out b/examples/error-reporting/error_reporting.out index 5fe7b170a3..609403ab5e 100644 --- a/examples/error-reporting/error_reporting.out +++ b/examples/error-reporting/error_reporting.out @@ -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] diff --git a/examples/error-subtyping/error_subtyping.bal b/examples/error-subtyping/error_subtyping.bal index d1d3a1e89e..5cfb3623b2 100644 --- a/examples/error-subtyping/error_subtyping.bal +++ b/examples/error-subtyping/error_subtyping.bal @@ -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; // `error` with `InvalidI32Detail` as the detail type. Thus it is a subtype of `InvalidIntError`. type InvalidI32Error error; -// `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; // 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; public function main() { @@ -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); } @@ -51,4 +51,3 @@ function createDistinctInvalidIntError(int value) returns DistinctIntError { function createInvalidI32Error(int:Signed32 value) returns InvalidI32Error { return error("Invalid i32", value = value); } - diff --git a/examples/error-subtyping/error_subtyping.md b/examples/error-subtyping/error_subtyping.md index 74112f9e95..6cd53ee3a6 100644 --- a/examples/error-subtyping/error_subtyping.md +++ b/examples/error-subtyping/error_subtyping.md @@ -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 diff --git a/examples/error-type-intersection/error_type_intersection.bal b/examples/error-type-intersection/error_type_intersection.bal index 832dc4820c..7049dcc62f 100644 --- a/examples/error-type-intersection/error_type_intersection.bal +++ b/examples/error-type-intersection/error_type_intersection.bal @@ -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`. diff --git a/examples/trap-expression/trap_expression.bal b/examples/trap-expression/trap_expression.bal index d9736e0edb..eb0297d7a4 100644 --- a/examples/trap-expression/trap_expression.bal +++ b/examples/trap-expression/trap_expression.bal @@ -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); } diff --git a/examples/trap-expression/trap_expression.md b/examples/trap-expression/trap_expression.md index 6ec7ca0adf..7699b557b6 100644 --- a/examples/trap-expression/trap_expression.md +++ b/examples/trap-expression/trap_expression.md @@ -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 ::: diff --git a/examples/trap-expression/trap_expression.out b/examples/trap-expression/trap_expression.out index d8e5f96579..84e1a561ae 100644 --- a/examples/trap-expression/trap_expression.out +++ b/examples/trap-expression/trap_expression.out @@ -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)