IO Operations
What are IO Operations?
IO (Input/Output) operations allow your functional programs to interact with the outside world. Baba Yaga provides a minimal set of IO operations that keep side effects contained and explicit.
Basic Output
Simple Output
/* Output values to console */
..out "Hello, World!";
..out 42;
..out true;
..out {name: "Alice", age: 30};
Output with Expressions
/* Output computed values */
result : 5 + 3 * 2;
..out result; /* Output: 11 */
/* Output function results */
double : x -> x * 2;
..out double 7; /* Output: 14 */
/* Output table operations */
numbers : {1, 2, 3, 4, 5};
doubled : map @double numbers;
..out doubled; /* Output: {2, 4, 6, 8, 10} */
Assertions
Assertions help you verify your program's behavior:
/* Basic assertions */
..assert 5 = 5; /* Passes */
..assert 3 + 2 = 5; /* Passes */
..assert true; /* Passes */
..assert false; /* Fails with error */
/* Assertions with messages */
..assert "5 equals 5" 5 = 5; /* Passes */
..assert "3 + 2 equals 5" 3 + 2 = 5; /* Passes */
..assert "This will fail" 1 = 2; /* Fails with message */
Testing Functions
/* Test function behavior */
factorial : n ->
when n is
0 then 1
_ then n * (factorial (n - 1));
/* Test cases */
..assert "factorial 0 = 1" factorial 0 = 1;
..assert "factorial 1 = 1" factorial 1 = 1;
..assert "factorial 5 = 120" factorial 5 = 120;
Emit and Listen Pattern
The ..emit
and ..listen
pattern provides a way to interface functional code with external systems:
Emitting Events
/* Emit events with data */
..emit "user_created" {id: 123, name: "Alice"};
..emit "data_processed" {count: 42, success: true};
..emit "error_occurred" {message: "Invalid input", code: 400};
Listening for Events
/* Listen for specific events */
..listen "user_created" handle_user_created;
..listen "data_processed" handle_data_processed;
..listen "error_occurred" handle_error;
Event Handlers
/* Define event handlers */
handle_user_created : user_data ->
..out "New user created:";
..out user_data.name;
handle_data_processed : result ->
when result.success is
true then ..out "Processing successful: " + result.count + " items"
false then ..out "Processing failed";
handle_error : error ->
..out "Error: " + error.message;
..out "Code: " + error.code;
Input Operations
Reading Input
/* Read input from user */
name : ..in "Enter your name: ";
..out "Hello, " + name + "!";
/* Read and process input */
age_input : ..in "Enter your age: ";
age : parseInt age_input;
..out "You are " + age + " years old";
IO Best Practices
Keep Side Effects Explicit
/* Good: Clear IO operations */
process_data : data ->
result : transform data;
..out "Processing complete";
..emit "data_processed" result;
result;
/* Avoid: Hidden side effects in pure functions */
bad_transform : data ->
..out "Processing..."; /* Side effect in "pure" function */
data * 2;
Use Assertions for Testing
/* Test your functions thoroughly */
is_even : x -> x % 2 = 0;
double : x -> x * 2;
/* Test individual functions */
..assert "0 is even" is_even 0 = true;
..assert "1 is not even" is_even 1 = false;
..assert "double 5 = 10" double 5 = 10;
/* Test composed functions */
doubled_evens : compose @double @is_even;
..assert "doubled_evens 6 = true" doubled_evens 6 = true;
Structured Output
/* Use tables for structured output */
user : {name: "Alice", age: 30, city: "NYC"};
..out "User Profile:";
..out " Name: " + user.name;
..out " Age: " + user.age;
..out " City: " + user.city;
/* Or output the entire structure */
..out user;
Common Patterns
Data Processing Pipeline
/* Process data with IO feedback */
data : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
..out "Processing " + t.length data + " items";
is_even : x -> x % 2 = 0;
double : x -> x * 2;
sum : x -> reduce @add 0 x;
/* Process with progress updates */
evens : filter @is_even data;
..out "Found " + t.length evens + " even numbers";
doubled : map @double evens;
..out "Doubled values:";
..out doubled;
total : sum doubled;
..out "Sum of doubled evens: " + total;
/* Emit final result */
..emit "processing_complete" {input_count: t.length data, result: total};
Error Handling
/* Handle potential errors gracefully */
safe_divide : x y ->
when y = 0 then
..emit "division_error" {dividend: x, divisor: y};
"Error: Division by zero"
_ then x / y;
/* Test error handling */
..out safe_divide 10 2; /* 5 */
..out safe_divide 10 0; /* Error: Division by zero */
Next Steps
Now that you understand IO operations, explore:
- Error Handling for robust error management
- Integration Patterns for external system integration
- Best Practices for writing clean code