# 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