diff options
Diffstat (limited to 'js/scripting-lang/tutorials/13_Error_Handling.md')
-rw-r--r-- | js/scripting-lang/tutorials/13_Error_Handling.md | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/js/scripting-lang/tutorials/13_Error_Handling.md b/js/scripting-lang/tutorials/13_Error_Handling.md new file mode 100644 index 0000000..07aff5a --- /dev/null +++ b/js/scripting-lang/tutorials/13_Error_Handling.md @@ -0,0 +1,256 @@ +# Error Handling + +## What is Error Handling? + +Error handling in Baba Yaga is based on functional programming principles - instead of throwing exceptions, we use pattern matching and return values to handle errors gracefully. + +## Basic Error Handling + +### Using Pattern Matching +```plaintext +/* Handle division by zero */ +safe_divide : x y -> + when y = 0 then "Error: Division by zero" + _ then x / y; + +/* Test the function */ +..out safe_divide 10 2; /* 5 */ +..out safe_divide 10 0; /* Error: Division by zero */ +``` + +### Return Error Values +```plaintext +/* Return structured error information */ +divide_with_error : x y -> + when y = 0 then {error: true, message: "Division by zero", dividend: x} + _ then {error: false, result: x / y}; + +/* Handle the result */ +result : divide_with_error 10 0; +when result.error is + true then ..out "Error: " + result.message + false then ..out "Result: " + result.result; +``` + +## Assertions for Validation + +### Input Validation +```plaintext +/* Validate function inputs */ +factorial : n -> + ..assert "n must be non-negative" n >= 0; + when n is + 0 then 1 + _ then n * (factorial (n - 1)); + +/* Test validation */ +..out factorial 5; /* 120 */ +/* factorial -1; */ /* Would fail assertion */ +``` + +### Data Validation +```plaintext +/* Validate table structure */ +validate_user : user -> + ..assert "user must have name" t.has user "name"; + ..assert "user must have age" t.has user "age"; + ..assert "age must be positive" user.age > 0; + user; + +/* Test validation */ +valid_user : {name: "Alice", age: 30}; +invalid_user : {name: "Bob"}; /* Missing age */ + +validated : validate_user valid_user; +/* validate_user invalid_user; */ /* Would fail assertion */ +``` + +## Error Patterns + +### Maybe Pattern +```plaintext +/* Maybe pattern for optional values */ +find_user : id users -> + when t.has users id then {just: true, value: t.get users id} + _ then {just: false}; + +/* Handle maybe results */ +users : { + alice: {name: "Alice", age: 30}, + bob: {name: "Bob", age: 25} +}; + +result : find_user "alice" users; +when result.just is + true then ..out "Found: " + result.value.name + false then ..out "User not found"; + +not_found : find_user "charlie" users; +when not_found.just is + true then ..out "Found: " + not_found.value.name + false then ..out "User not found"; +``` + +### Either Pattern +```plaintext +/* Either pattern for success/error */ +parse_number : input -> + parsed : parseInt input; + when parsed = NaN then {left: "Invalid number: " + input} + _ then {right: parsed}; + +/* Handle either results */ +valid : parse_number "42"; +when valid.left is + _ then ..out "Error: " + valid.left + _ then ..out "Success: " + valid.right; + +invalid : parse_number "abc"; +when invalid.left is + _ then ..out "Error: " + invalid.left + _ then ..out "Success: " + invalid.right; +``` + +## Error Recovery + +### Fallback Values +```plaintext +/* Provide fallback values */ +get_config : key default_value config -> + when t.has config key then t.get config key + _ then default_value; + +/* Use with fallbacks */ +config : {debug: true, timeout: 30}; +debug_mode : get_config "debug" false config; /* true */ +retries : get_config "retries" 3 config; /* 3 (fallback) */ +``` + +### Retry Logic +```plaintext +/* Simple retry with exponential backoff */ +retry_operation : operation max_attempts -> + attempt_operation : attempt -> + when attempt > max_attempts then {error: "Max attempts exceeded"} + _ then + result : operation; + when result.error is + true then + delay : power 2 attempt; /* Exponential backoff */ + ..out "Attempt " + attempt + " failed, retrying in " + delay + "ms"; + attempt_operation (attempt + 1) + false then result; + + attempt_operation 1; +``` + +## Error Propagation + +### Chaining Error Handling +```plaintext +/* Chain operations that might fail */ +process_user_data : user_id -> + /* Step 1: Find user */ + user_result : find_user user_id users; + when user_result.just is + false then {error: "User not found: " + user_id} + _ then + user : user_result.value; + + /* Step 2: Validate user */ + validation_result : validate_user user; + when validation_result.error is + true then {error: "Invalid user data"} + _ then + /* Step 3: Process user */ + processed : process_user user; + {success: true, data: processed}; +``` + +## Testing Error Conditions + +### Test Error Cases +```plaintext +/* Test both success and error cases */ +test_safe_divide : -> + /* Test successful division */ + ..assert "10 / 2 = 5" safe_divide 10 2 = 5; + + /* Test division by zero */ + error_result : safe_divide 10 0; + ..assert "Division by zero returns error" error_result = "Error: Division by zero"; + + ..out "All tests passed"; + +/* Run the tests */ +test_safe_divide; +``` + +### Property-Based Testing +```plaintext +/* Test properties of error handling */ +test_divide_properties : -> + /* Property: safe_divide x 1 = x */ + ..assert "x / 1 = x" safe_divide 42 1 = 42; + + /* Property: safe_divide x 0 always returns error */ + ..assert "x / 0 always errors" safe_divide 5 0 = "Error: Division by zero"; + ..assert "x / 0 always errors" safe_divide -3 0 = "Error: Division by zero"; + + /* Property: safe_divide 0 x = 0 (when x ≠ 0) */ + ..assert "0 / x = 0" safe_divide 0 5 = 0; + + ..out "All properties verified"; +``` + +## Best Practices + +### Keep Error Handling Explicit +```plaintext +/* Good: Explicit error handling */ +process_data : data -> + when data = null then {error: "No data provided"} + _ then + result : transform data; + when result.error is + true then result + false then {success: true, data: result.data}; + +/* Avoid: Silent failures */ +bad_process : data -> + transform data; /* What if this fails? */ +``` + +### Use Descriptive Error Messages +```plaintext +/* Good: Descriptive errors */ +validate_age : age -> + when age < 0 then "Age cannot be negative: " + age + when age > 150 then "Age seems unrealistic: " + age + _ then age; + +/* Avoid: Generic errors */ +bad_validate : age -> + when age < 0 then "Invalid input" /* Too generic */ + _ then age; +``` + +### Handle Errors at the Right Level +```plaintext +/* Handle errors where you have context */ +process_user : user_id -> + user : find_user user_id; + when user.just is + false then + ..emit "user_not_found" {user_id: user_id, timestamp: now()}; + "User not found" + _ then + process_user_data user.value; +``` + +## Next Steps + +Now that you understand error handling, explore: +- [Integration Patterns](15_Integration_Patterns.md) for external system error handling +- [Advanced Combinators](14_Advanced_Combinators.md) for error handling patterns +- [Best Practices](16_Best_Practices.md) for writing robust code \ No newline at end of file |