diff options
Diffstat (limited to 'js/baba-yaga/docs')
-rw-r--r-- | js/baba-yaga/docs/00_crash-course.md | 986 | ||||
-rw-r--r-- | js/baba-yaga/docs/01_functional.md | 235 | ||||
-rw-r--r-- | js/baba-yaga/docs/02_data-structures.md | 133 | ||||
-rw-r--r-- | js/baba-yaga/docs/03_pattern-matching.md | 133 | ||||
-rw-r--r-- | js/baba-yaga/docs/04_types.md | 196 | ||||
-rw-r--r-- | js/baba-yaga/docs/05_recursion-and-composition.md | 136 | ||||
-rw-r--r-- | js/baba-yaga/docs/06_error-handling.md | 632 | ||||
-rw-r--r-- | js/baba-yaga/docs/07_gotchyas.md | 642 | ||||
-rw-r--r-- | js/baba-yaga/docs/08_array-programming.md | 320 | ||||
-rw-r--r-- | js/baba-yaga/docs/09_js-interop.md | 500 | ||||
-rw-r--r-- | js/baba-yaga/docs/README.md | 82 | ||||
-rw-r--r-- | js/baba-yaga/docs/ref.txt | 213 |
12 files changed, 4208 insertions, 0 deletions
diff --git a/js/baba-yaga/docs/00_crash-course.md b/js/baba-yaga/docs/00_crash-course.md new file mode 100644 index 0000000..0327e5e --- /dev/null +++ b/js/baba-yaga/docs/00_crash-course.md @@ -0,0 +1,986 @@ +# Baba Yaga Crash Course + +## Language Overview +Baba Yaga is a functional scripting language emphasizing immutability, pattern matching, and explicit error handling. It features currying, anonymous functions, recursive functions, and a useful `when` expression for control flow. + +## Core Syntax Rules + +### Comments and Statements +```baba +// Single line comments start with // + +variable : 42; // Variable declaration +function : x -> x + 1; // Function declaration +``` + +### Data Types and Literals + +**Numbers:** +```baba +integerValue : 42; // Int type +floatValue : 3.14; // Float type +negativeValue : -10; // Negative numbers +``` + +**Strings:** +```baba +greeting : "Hello World"; // String type +concatenated : "Hello" .. " " .. "World"; // String concatenation with .. or str.concat +``` + +**Booleans:** +```baba +isTrue : true; +isFalse : false; +``` + +**Mathematical Constants:** +```baba +pi : PI; // Built-in π constant +infinity : INFINITY; // Built-in infinity +``` + +**Lists (Immutable):** +```baba +numbers : [1, 2, 3, 4]; +mixed : [1, "hello", true]; +firstElement : numbers.0; // Zero-based indexing +secondElement : numbers.1; +``` + +**Tables (Immutable Key-Value):** +```baba +person : {name: "Lucy Snowe", age: 23, active: true}; +userName : person.name; // Property access +userAge : person.age; +``` + +### Type System and Declarations + +**Optional Type Annotations:** +```baba +// Optional type declaration +myNumber Int; +myNumber : 42; + +// Direct assignment with type inference +greeting : "Hello"; // Type inferred as String +``` + +**Type Hierarchy:** +- `Int` ⊂ `Float` ⊂ `Number` (`Int` can be used where `Float` expected) +- `String`, `Bool`, `List`, `Table`, `Result` are distinct types + +## Functions + +### Basic Functions +```baba +// Simple function +add : x y -> x + y; +result : add 5 3; // Function call: 8 + +// Single parameter function +square : x -> x * x; +squared : square 4; // 16 +``` + +### Anonymous Functions +```baba +// Anonymous function +doubler : x -> x * 2; + +// Immediately invoked +result : (x -> x + 1) 5; // 6 + +// In table literals +calculator : { + add: x y -> x + y; + multiply: x y -> x * y; +}; +sum : calculator.add 10 20; // 30 +``` + +### Currying and Partial Application +```baba +// Curried function (multiple arrows) +add : x -> y -> x + y; +add5 : add 5; // Partial application +result : add5 3; // 8 + +// Multi-level currying +addThree : x -> y -> z -> x + y + z; +addTwoMore : addThree 1; // Partially applied +addOne : addTwoMore 2; // Partially applied +final : addOne 3; // 6 +``` + +### Typed Functions +```baba +// Function with parameter and return types +add : (x: Int, y: Int) -> Int -> x + y; +multiply : (x: Float, y: Float) -> Float -> x * y; + +// Curried typed function +curriedAdd : (x: Int) -> (Int -> Int) -> y -> x + y; +``` + +### Recursive Functions +```baba +// Simple recursion +factorial : n -> + when n is + 0 then 1 + 1 then 1 + _ then n * (factorial (n - 1)); + +// Fibonacci +fibonacci : n -> + when n is + 0 then 0 + 1 then 1 + _ then (fibonacci (n - 1)) + (fibonacci (n - 2)); +``` + +### Local Bindings (`with`/`with rec`) + +```baba +// Non-recursive local bindings +addMul : x y -> with (inc : x + 1; prod : inc * y;) -> inc + prod; + +// Typed locals +sumNext : (x: Int, y: Int) -> Int -> + with (nx Int; ny Int; nx : x + 1; ny : y + 1;) -> nx + ny; + +// Mutually recursive locals +evenOdd : z -> with rec ( + isEven : n -> when n is 0 then true _ then isOdd (n - 1); + isOdd : n -> when n is 0 then false _ then isEven (n - 1); +) -> {even: isEven z, odd: isOdd z}; +``` + +**Common Patterns and Use Cases:** + +```baba +// 1. Computed intermediate values (avoiding repetition) +quadraticRoots : a b c -> + with ( + discriminant : b * b - 4 * a * c; + sqrtDisc : math.sqrt discriminant; + denominator : 2 * a; + ) -> + { + r1: (-b + sqrtDisc) / denominator, + r2: (-b - sqrtDisc) / denominator + }; + +// 2. Complex calculations with named steps +calculateTax : income deductions -> + with ( + taxableIncome : income - deductions; + taxRate : when (taxableIncome <= 50000) is + true then 0.15 + _ then when (taxableIncome <= 100000) is + true then 0.25 + _ then 0.35; + baseTax : taxableIncome * taxRate; + finalTax : when (baseTax < 1000) is true then 1000 _ then baseTax; + ) -> + finalTax; + +// 3. Data transformation pipelines +processUserData : user -> + with ( + normalizedName : str.upper (str.trim user.name); + ageGroup : when (user.age < 18) is + true then "minor" + _ then when (user.age < 65) is + true then "adult" + _ then "senior"; + status : when user.active is + true then "active" + false then "inactive"; + ) -> + { + id: user.id, + displayName: normalizedName, + category: ageGroup, + status: status + }; + +// 4. Error handling with multiple validations +validateOrder : order -> + with ( + hasItems : (length order.items) > 0; + hasValidTotal : order.total > 0; + // Note: Baba Yaga doesn't have null, so we'll use a different validation + hasValidShipping : (length order.shippingAddress) > 0; + allValid : hasItems and hasValidTotal and hasValidShipping; + ) -> + when allValid is + true then Ok order + false then Err "Order validation failed"; + +// 5. Complex pattern matching with computed values +classifyTriangle : a b c -> + with ( + sorted : [math.min a b, math.max a b, math.max (math.max a b) c]; + side1 : sorted.0; + side2 : sorted.1; + side3 : sorted.2; + isValid : ((side1 + side2) > side3); + isEquilateral : ((a = b) and (b = c)); + isIsosceles : ((a = b) or (b = c) or (a = c)); + isRight : (math.abs ((side1 * side1 + side2 * side2) - (side3 * side3))) < 0.001; + ) -> + when isValid is + false then "Invalid triangle" + true then when isEquilateral is + true then "Equilateral" + false then when isIsosceles is + true then when isRight is + true then "Right isosceles" + false then "Isosceles" + false then when isRight is + true then "Right scalene" + false then "Scalene"; + +// 6. Mutually recursive functions (with rec) +// Binary tree operations +treeOperations : tree -> + with rec ( + // Count total nodes + countNodes : t -> + when (isEmptyTable t) is + true then 0 + false then 1 + (countNodes t.left) + (countNodes t.right); + + // Calculate tree height + treeHeight : t -> + when (isEmptyTable t) is + true then 0 + false then 1 + (math.max (treeHeight t.left) (treeHeight t.right)); + + // Check if tree is balanced + isBalanced : t -> + when (isEmptyTable t) is + true then true + false then + (math.abs ((treeHeight t.left) - (treeHeight t.right)) <= 1) and + (isBalanced t.left) and + (isBalanced t.right); + ) -> + { + nodeCount: countNodes tree, + height: treeHeight tree, + balanced: isBalanced tree + }; + +// 7. State machine with recursive state transitions +trafficLight : initialState -> + with rec ( + // State transition function + nextState : current -> + when current is + "red" then "green" + "green" then "yellow" + "yellow" then "red"; + + // Count transitions until back to start + countCycles : start current count -> + when current = start is + true then count + false then countCycles start (nextState current) (count + 1); + + // Get state after N transitions + stateAfter : current n -> + when n is + 0 then current + _ then stateAfter (nextState current) (n - 1); + ) -> + { + cycles: countCycles initialState initialState 0, + after10: stateAfter initialState 10, + next: nextState initialState + }; + +// 8. Combinatorial functions with shared helpers +combinatorics : n r -> + with rec ( + // Factorial function + factorial : k -> + when k is + 0 then 1 + 1 then 1 + _ then k * (factorial (k - 1)); + + // Permutation: P(n,r) = n! / (n-r)! + permutation : n r -> + factorial n / (factorial (n - r)); + + // Combination: C(n,r) = n! / (r! * (n-r)!) + combination : n r -> + factorial n / ((factorial r) * (factorial (n - r))); + ) -> + { + n: n, + r: r, + permutations: permutation n r, + combinations: combination n r + }; +``` + +**Key Benefits of `with` and `with rec`:** + +1. **Avoid Repetition**: Compute values once and reuse them +2. **Improve Readability**: Give meaningful names to intermediate calculations +3. **Enable Complex Logic**: Break down complex operations into clear steps +4. **Mutual Recursion**: `with rec` allows local functions to call each other +5. **Scoped Variables**: Local bindings don't pollute global scope +6. **Type Safety**: Can declare types for local variables +7. **Performance**: Avoid recalculating expensive operations +8. **Maintainability**: Centralize related logic in one place + +**When to Use `with` vs `with rec`:** + +- **Use `with`** when you need local bindings for computed values, intermediate results, or simple helper functions that don't call each other +- **Use `with rec`** when you need mutually recursive functions or when local functions need to reference each other + +**Best Practices:** + +```baba +// Good: Clear, focused with blocks +processData : data -> + with ( + cleaned : str.trim data; + normalized : str.lower cleaned; + validated : (length normalized) > 0; + ) -> + when validated is + true then Ok normalized + false then Err "Empty data"; + +// Love it: Logical grouping of related operations +analyzeNumbers : numbers -> + with ( + count : length numbers; + sum : reduce (acc x -> acc + x) 0 numbers; + average : when count > 0 then sum / count _ else 0; + sorted : sort numbers; + median : when count % 2 = 0 then + (sorted.(count / 2 - 1) + sorted.(count / 2)) / 2 + _ else sorted.(count / 2); + ) -> + {count, sum, average, median}; + +// Oh no! Avoid: Overly complex with blocks +// Instead, break into smaller functions +processUser : user -> + with ( + // Too many bindings - consider breaking into helper functions + nameValid : (length user.name) > 0; + emailValid : (length user.email) > 0; // Simplified validation + ageValid : (user.age >= 0) and (user.age <= 150); + phoneValid : (length user.phone) >= 10; + addressValid : (length user.address) > 0; // Simplified validation + preferencesValid : (length user.preferences) > 0; + allValid : (nameValid and emailValid and ageValid and phoneValid and addressValid and preferencesValid); + ) -> + when allValid is + true then Ok user + _ then Err "Validation failed"; + +// Better: Break into focused functions +validateUserBasic : user -> + with ( + nameValid : (length user.name) > 0; + emailValid : (length user.email) > 0; // Simplified validation + ageValid : (user.age >= 0) and (user.age <= 150); + ) -> + (nameValid and emailValid and ageValid); + +validateUserContact : user -> + with ( + phoneValid : (length user.phone) >= 10; + // Note: Baba Yaga doesn't have null, so we'll use a different validation + addressValid : (length user.address) > 0; + ) -> + (phoneValid and addressValid); + +processUser : user -> + when ((validateUserBasic user) and (validateUserContact user)) is + true then Ok user + _ then Err "Validation failed"; +``` + +**Common Anti-patterns to Avoid:** + +```baba +// Don't: Use with for simple expressions +badExample : x -> + with (result : x + 1;) -> result; // Just use: x -> x + 1 + +// Don't: Nest with blocks unnecessarily +nestedBad : x -> + with (a : x + 1;) -> + with (b : a * 2;) -> + with (c : b + 3;) -> c; // Use: with (a: x+1; b: a*2; c: b+3;) -> c + +// Don't: Use with rec when simple recursion will do the trick +simpleRecursion : n -> + when n is + 0 then 1 + _ then n * (simpleRecursion (n - 1)); // No need for with rec + +// Do: Use with rec for mutual recursion +mutualRecursion : n -> + with rec ( + isEven : x -> when x is 0 then true _ then isOdd (x - 1); + isOdd : x -> when x is 0 then false _ then isEven (x - 1); + ) -> + isEven n; +``` + +## Pattern Matching with `when` + +### Basic Pattern Matching +```baba +// Literal patterns +checkNumber : num -> + when num is + 0 then "Zero" + 1 then "One" + 2 then "Two" + _ then "Other"; // Wildcard matches anything +``` + +### Multiple Discriminants +```baba +checkCoords : x y -> + when x y is + 0 0 then "Origin" + 1 1 then "Diagonal" + _ _ then "Somewhere else"; +``` + +### Type Patterns +```baba +checkType : value -> + when value is + Int then "It's an integer" + String then "It's a string" + Bool then "It's a boolean" + _ then "Unknown type"; +``` + +### Result Pattern Matching +```baba +processResult : result -> + when result is + Ok value then value * 2 // 'value' binds to inner data + Err message then 0; // 'message' binds to error string +``` + +## Error Handling with Result Type + +Baba Yaga uses the `Result` type for explicit error handling instead of exceptions. See [Error Handling](./06_error-handling.md) for comprehensive coverage. + +```baba +// Function returning Result +divide : x y -> + when y is + 0 then Err "Division by zero" + _ then Ok (x / y); + +// Using Result +result : divide 10 2; // Ok 5 +errorResult : divide 5 0; // Err "Division by zero" + +// Pattern matching on Result +handleDivision : x y -> + when (divide x y) is + Ok value then value + Err msg then 0; +``` + +## Operators and Precedence + +### Arithmetic Operators +```baba +addition : 5 + 3; // 8 +subtraction : 10 - 4; // 6 +multiplication : 6 * 7; // 42 +division : 15 / 3; // 5 +modulo : 17 % 5; // 2 +unaryMinus : -42; // -42 +``` + +### Comparison Operators +```baba +equal : 5 = 5; // true +notEqual : 5 != 3; // true +greater : 5 > 3; // true +less : 3 < 5; // true +greaterEqual : 5 >= 5; // true +lessEqual : 3 <= 5; // true +``` + +### Logical Operators +```baba +andResult : true and false; // false +orResult : true or false; // true +xorResult : true xor true; // false +``` + +### String Concatenation +```baba +combined : "Hello" .. " " .. "World"; // "Hello World" +``` + +### Operator Precedence (highest to lowest) +1. `*`, `/`, `%` (multiplication, division, modulo) +2. `+`, `-` (addition, subtraction) +3. `=`, `!=`, `>`, `<`, `>=`, `<=` (comparison) +4. `xor` +5. `and` +6. `or` +7. `..` (string concatenation) + +## Built-in Functions + +### Higher-Order List Functions +```baba +numbers : [1, 2, 3, 4, 5]; + +// map: apply function to each element +doubled : map (x -> x * 2) numbers; // [2, 4, 6, 8, 10] + +// filter: keep elements matching predicate +evens : filter (x -> x % 2 = 0) numbers; // [2, 4] + +// reduce: fold list to single value +sum : reduce (acc x -> acc + x) 0 numbers; // 15 +``` + +### Immutable List Operations +```baba +original : [1, 2, 3]; + +// append: add to end +withFour : append original 4; // [1, 2, 3, 4] + +// prepend: add to beginning +withZero : prepend 0 original; // [0, 1, 2, 3] + +// concat: combine lists +combined : concat [1, 2] [3, 4]; // [1, 2, 3, 4] + +// update: replace at index +updated : update original 1 99; // [1, 99, 3] + +// removeAt: remove at index +removed : removeAt original 0; // [2, 3] + +// slice: extract sublist +sublist : slice original 0 2; // [1, 2] + +// Note: original list is never modified! +``` + +### Immutable Table Operations +```baba +user : {name: "Alice", age: 30}; + +// set: add/update property +updated : set user "city" "Boston"; // {name: "Alice", age: 30, city: "Boston"} + +// remove: delete property +minimal : remove user "age"; // {name: "Alice"} + +// merge: combine tables (second overrides first) +merged : merge user {country: "USA"}; // {name: "Alice", age: 30, country: "USA"} + +// keys: get all keys as list +keyList : keys user; // ["name", "age"] + +// values: get all values as list +valueList : values user; // ["Alice", 30] +``` + +### String Operations +```baba +// String concatenation +combined : str.concat "Hello" " " "World"; // "Hello World" + +// Split string into list +words : str.split "a,b,c" ","; // ["a", "b", "c"] + +// Join list into string +sentence : str.join ["Hello", "World"] " "; // "Hello World" + +// String length +len : str.length "Hello"; // {value: 5, isFloat: false} + +// Substring +part : str.substring "Hello World" 0 5; // "Hello" + +// Replace +replaced : str.replace "Hello World" "World" "Universe"; // "Hello Universe" + +// Trim whitespace +clean : str.trim " Hello "; // "Hello" + +// Case conversion +upper : str.upper "hello"; // "HELLO" +lower : str.lower "WORLD"; // "world" +``` + +### Math Operations +```baba +// Basic math functions +absolute : math.abs -5; // {value: 5, isFloat: true} +minimum : math.min 3 7; // {value: 3, isFloat: true} +maximum : math.max 3 7; // {value: 7, isFloat: true} +clamped : math.clamp 10 0 5; // {value: 5, isFloat: true} + +// Rounding +floored : math.floor 3.7; // {value: 3, isFloat: true} +ceiled : math.ceil 3.2; // {value: 4, isFloat: true} +rounded : math.round 3.5; // {value: 4, isFloat: true} + +// Powers and roots +powered : math.pow 2 3; // {value: 8, isFloat: true} +squareRoot : math.sqrt 16; // {value: 4, isFloat: true} + +// Trigonometry (radians) +sine : math.sin 0; // {value: 0, isFloat: true} +cosine : math.cos 0; // {value: 1, isFloat: true} + +// Random numbers +random : math.random; // Random float 0 <= x < 1 +randomInt : math.randomInt 1 6; // Random integer 1-6 inclusive +``` + +### Enhanced Random Operations +```baba +// Enhanced random utilities +numbers : [1, 2, 3, 4, 5]; +choice : random.choice numbers; // Random element from list +shuffled : random.shuffle numbers; // Shuffled copy of list +randomNum : random.range 1 10; // Random integer 1-10 +random.seed 42; // Set random seed (placeholder) +``` + +### Data Validation +```baba +// Input validation utilities +isValid : validate.notEmpty "hello"; // true +isEmpty : validate.notEmpty ""; // false +inRange : validate.range 1 10 5; // true +validEmail : validate.email "user@domain.com"; // true +correctType : validate.type "Int" 42; // true +``` + +### Text Processing +```baba +// Enhanced text utilities +multiline : "line1\nline2\nline3"; // Note: \n is literal, not newline +lines : text.lines multiline; // ["line1\\nline2\\nline3"] (single item) +words : text.words "hello world test"; // ["hello", "world", "test"] +padded : text.padLeft 10 "hi"; // " hi" +aligned : text.padRight 10 "hi"; // "hi " +``` + +### Data Transformation +```baba +// Sorting with custom criteria +people : [ + {name: "Alice", age: 30}, + {name: "Bob", age: 25}, + {name: "Charlie", age: 35} +]; +byAge : sort.by people (p -> p.age); // Sorted by age: Bob, Alice, Charlie + +// Grouping data +numbers : [1, 2, 3, 4, 5, 6]; +grouped : group.by numbers (x -> x % 2 = 0); // Groups by even/odd +evenNums : grouped."true"; // [2, 4, 6] +oddNums : grouped."false"; // [1, 3, 5] +``` + +### Utility Functions +```baba +// Array chunking (APL-style windowing) +data : [1, 2, 3, 4, 5, 6]; +chunks : chunk data 2; // [[1, 2], [3, 4], [5, 6]] + +// Range generation +sequence : range 1 5; // [1, 2, 3, 4, 5] +countdown : range 5 1; // [5, 4, 3, 2, 1] + +// Value repetition +repeated : repeat 3 "hello"; // ["hello", "hello", "hello"] +``` + +### Debug and Development Tools +```baba +// Enhanced debugging with type information +debug.print 42; // [DEBUG] 42 (Int) +debug.print (x -> x * 2); // [DEBUG] <function: (x) -> ...> (Unknown) + +// Detailed value inspection +myFunc : x -> x + 1; +details : debug.inspect myFunc; // Returns detailed type information + +// Assertions with custom messages +assert (2 + 2 = 4) "Math should work"; // Passes silently +// assert (2 + 2 = 5) "This fails"; // Throws: "Assertion failed: This fails" +``` + +### I/O Operations + +**Basic Output:** +```baba +// Output to console +io.out "Hello World"; +io.out 42; +io.out [1, 2, 3]; + +// Read input (returns string) +input : io.in; +``` + +**Enhanced Output with `io.print`:** + +The `io.print` function provides formatting for different data types, making output more easily readable: + +```baba +// Automatic grid detection for 2D arrays +gameBoard : [ + [1, 0, 1], + [0, 1, 0], + [1, 0, 1] +]; + +io.print gameBoard; +// Output: +// █·█ +// ·█· +// █·█ + +// Labeled output with format strings +io.print "Game Board" gameBoard; +io.print "Score" 1500; +io.print "Player" "Alice"; + +// Perfect for Conway's Game of Life, chess boards, mazes, etc. +glider : [ + [0, 1, 0, 0, 0], + [0, 0, 1, 0, 0], + [1, 1, 1, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0] +]; + +io.print "Glider Pattern Evolution:"; +io.print "Step 0:"; +io.print glider; +// Output: +// Glider Pattern Evolution: +// Step 0: +// ·█··· +// ··█·· +// ███·· +// ····· +// ····· + +// Enhanced display of other data types +myFunction : x -> x * 2; +result : Ok 42; +error : Err "Something went wrong"; + +io.print "Function" myFunction; // Function: <function> +io.print "Success" result; // Success: Ok(42) +io.print "Failure" error; // Failure: Err(Something went wrong) +``` + +**Key `io.print` Features:** +- **Automatic Grid Detection**: 2D numeric arrays display as visual grids with `█` (alive/1) and `·` (dead/0) +- **Clean Function Display**: Shows `<function>` instead of complex internal representations +- **Enhanced Result Types**: Displays `Ok(value)` and `Err(message)` in readable format +- **Labeled Output**: Use format strings like `io.print "Label" data` for organized output +- **Educational Value**: Perfect for teaching algorithms, game development, data visualization +- **Backward Compatible**: Works as a drop-in replacement for `io.out` in most cases + +### Introspection +```baba +// Get shape information about values +listInfo : shape [1, 2, 3]; +// Returns: {kind: "List", rank: 1, shape: [3], size: 3, isEmpty: false} + +stringInfo : shape "hello"; +// Returns: {kind: "String", rank: 1, shape: [5], size: 5, isEmpty: false} + +tableInfo : shape {a: 1, b: 2}; +// Returns: {kind: "Table", rank: 1, shape: [2], size: 2, keys: ["a", "b"], isEmpty: false} +``` + +## Complete Examples + +### Calculator with Pattern Matching +```baba +calculate : op x y -> + when op is + "add" then (x + y) + "subtract" then (x - y) + "multiply" then (x * y) + "divide" then + when y is + 0 then Err "Division by zero" + _ then Ok (x / y) + _ then Err "Unknown operation"; + +result1 : calculate "add" 5 3; // 8 +result2 : calculate "divide" 10 0; // Err "Division by zero" +``` + +### List Processing Pipeline +```baba +numbers : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + +// Find sum of squares of even numbers +evenSquareSum : reduce (acc x -> acc + x) 0 + (map (x -> x * x) + (filter (x -> x % 2 = 0) numbers)); +// Result: 220 (4 + 16 + 36 + 64 + 100) +``` + +### Tree Processing with Recursion +```baba +isEmptyTable : t -> (length (keys t)) = 0; + +treeHeight : tree -> + when (isEmptyTable tree) is + true then 0 + false then + when (isEmptyTable tree.left) (isEmptyTable tree.right) is + true true then 1 + _ _ then 1 + (math.max (treeHeight tree.left) + (treeHeight tree.right)); + +myTree : { +value: 1, + left: {value: 2, left: {}, right: {}}, + right: {value: 3, left: {}, right: {}} +}; + +height : treeHeight myTree; // 2 +``` + +### Error Handling Chain +```baba +// Chain of operations that might fail +processData : input -> + when (parseNumber input) is + Err msg then Err msg + Ok num then + when (num / 2) > 10 is + true then Ok (num / 2) + false then Err "Result too small"; + +parseNumber : str -> + when str is + "0" then Ok 0 + "10" then Ok 10 + "20" then Ok 20 + _ then Err "Invalid number"; + +result : processData "20"; // Returns Err "Result too small" (10 is not > 10) +``` + +## Key Patterns + +1. **Immutability**: All data structures are immutable. Use built-in functions like `append`, `set`, etc. +2. **Pattern Matching**: Use `when` expressions for control flow instead of if/else chains. +3. **Error Handling**: Use `Result` type with `Ok`/`Err` variants instead of exceptions. +4. **Function Composition**: Build complex behavior by composing simple functions. +5. **Recursion**: Use recursive functions with pattern matching for iteration-like behavior. +6. **Type Safety**: Use optional type annotations for better error detection. + +## Function Call Syntax + +- Parentheses are optional: `add 5 3` or `(add 5 3)` +- Function calls are left-associative: `f x y z` means `((f x) y) z` +- Use parentheses to group: `f (g x) y` applies `g` to `x` first + +## Misc. Important Notes + +- **Whitespace**: Significant for function calls (spaces separate arguments) +- **Mutability**: Everything is immutable - operations return new values +- **Type Coercion**: `Int` automatically promotes to Float when needed +- **Error Strategy**: Use `Result` type, not exceptions +- **Recursion**: Preferred over loops for iteration +- **Variables**: Actually immutable bindings, not mutable variables + +## Execution Model and Environment + +- **Entry point**: Programs execute top-to-bottom at the file’s top level. There is no required `main` function; define and call one yourself if needed or if you like them. +- **Modules**: No built-in import/export system. Larger programs are typically single-file or orchestrated by a host embedding (see `../IO.md`, WIP). +- **Standard library scope**: + - In addition to items earlier, available built-ins include: + - **General**: `length` (works on lists and strings), `chunk`, `range`, `repeat`, `assert` + - **Array Programming**: `scan`, `cumsum`, `cumprod`, `at`, `where`, `take`, `drop`, `broadcast`, `zipWith`, `reshape`, `flatMap` + - **Function Combinators**: `flip`, `apply`, `pipe`, `compose` + - **String namespace `str`**: `concat`, `split`, `join`, `length`, `substring`, `replace`, `trim`, `upper`, `lower` + - **Text namespace `text`**: `lines`, `words`, `padLeft`, `padRight` + - **Math namespace `math`**: `abs`, `sign`, `floor`, `ceil`, `round`, `trunc`, `min`, `max`, `clamp`, `pow`, `sqrt`, `exp`, `log`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2`, `deg`, `rad`, `random`, `randomInt` + - **Random namespace `random`**: `choice`, `shuffle`, `range`, `seed` + - **Validation namespace `validate`**: `notEmpty`, `range`, `email`, `type` + - **Sorting namespace `sort`**: `by` + - **Grouping namespace `group`**: `by` + - **Debug namespace `debug`**: `print`, `inspect` + - **IO namespace `io`**: `out`, `in`, `print`, and (host-enabled) event functions `io.emit`, `io.listen` + +## Syntax Clarifications + +- **Statement termination**: Top-level statements, table fields, and parentheses bodies accept a trailing `;` if present. +- **Whitespace**: Only significant for function calls (spaces separate arguments). Indentation/newlines do not affect `when` or other constructs. +- **Operator associativity**: All binary operators (arithmetic, comparison, logical, `..`) are left-associative. Precedence is described earlier in this document, and also in the [Types](./04_types.md) and [Gotchyas](./07_gotchyas.md) documentation. + +## Type System Details + +- **Type inference**: Type annotations are optional everywhere. They provide runtime validation; without them, code is dynamically typed. +- **Numeric hierarchy**: `Int ⊂ Float ⊂ Number` (widening permitted). +- **Generics**: No type parameters/generics. Achieve polymorphism by leaving parameters unannotated. +- **Type aliases**: Not supported. + +## Advanced Pattern Matching + +- **Nested patterns**: Supported for lists and tables. You can match deep structures, e.g. `{user: {name: "Tziporah"}}`. +- **Pattern Guards**: Use `if` keyword to add conditions to patterns: + ```baba + classify : x -> + when x is + n if (n > 0) then "positive" + n if (n < 0) then "negative" + 0 then "zero"; + ``` +- **Lists/strings**: List patterns match exact shapes (no head/tail cons syntax). Strings can be matched by literal or `String` type. + +## Function Behavior Edge Cases + +- **Partial application**: Supported for all functions; supplying fewer args returns a new function. Supplying more than arity is an error. +- **Recursion optimization**: Tail-call optimization is not performed. +- **Closures and `with`**: + - Functions capture a snapshot of surrounding scope at definition time. + - `with rec` creates function bindings that close over a shared scope (by reference) to enable mutual recursion. + +## Error Handling Patterns + +- **Result chaining**: No built-ins like `andThen`/`mapError`. Idiomatically, compose with helper functions or use nested `when`. +- **Runtime errors**: Thrown as plain errors with standard messages (e.g., `Division by zero`, `Index out of bounds`, `Undefined variable`, `Undefined property`, `Unknown operator`). + +## Performance & Limits + +- **Stack limits**: Recursion depth is limited by the host JS engine’s stack (no TCO). +- **Data sizes**: Lists are JS arrays; tables are proxied maps/objects. Limits are memory-bound. +- **Immutability costs**: Operations return new arrays/maps (no structural sharing). Favor `map`/`filter`/`reduce` and careful composition for large data. diff --git a/js/baba-yaga/docs/01_functional.md b/js/baba-yaga/docs/01_functional.md new file mode 100644 index 0000000..ac8134e --- /dev/null +++ b/js/baba-yaga/docs/01_functional.md @@ -0,0 +1,235 @@ +# Functional Programming with Baba Yaga + +Baba Yaga is expression-oriented, immutable by default, and built around simple, first-class functions. + +## Core Concepts + +- Immutability: lists and tables return new values for every change +- First-class functions: functions can be passed, returned, and stored in tables +- Expressions: everything is an expression, including `when` + +## Anonymous Functions, Currying, Partial Application + +```baba +// Anonymous function +inc : (x -> x + 1); + +// Curried function (equivalent to x -> (y -> x + y)) +add : x -> y -> x + y; + +// Partial application +after5 : add 5; // after5 is a function (y -> 5 + y) +result : after5 10; // 15 +``` + +## Higher-Order Functions + +Built-ins for lists: `map`, `filter`, `reduce`. +```baba +doubled : map (x -> x * 2) [1, 2, 3]; // [2, 4, 6] +evens : filter (x -> x % 2 = 0) [1, 2, 3, 4, 5]; // [2, 4] +sum : reduce (acc x -> acc + x) 0 [1, 2, 3, 4]; // 10 +``` + +## Advanced Data Operations + +Enhanced utilities for sorting and grouping: +```baba +// Custom sorting with key functions +students : [ + {name: "Alice", grade: 85}, + {name: "Bob", grade: 92}, + {name: "Charlie", grade: 78} +]; +byGrade : sort.by students (s -> s.grade); // Sorted by grade: Charlie, Alice, Bob + +// Grouping data by criteria +ages : [18, 25, 17, 30, 16, 45]; +byCategory : group.by ages (age -> + when (age < 18) is + true then "minor" + _ then when (age < 65) is + true then "adult" + _ then "senior" +); +minors : byCategory."minor"; // [17, 16] +adults : byCategory."adult"; // [18, 25, 30, 45] + +// Array processing utilities +data : [1, 2, 3, 4, 5, 6, 7, 8]; +chunks : chunk data 3; // [[1, 2, 3], [4, 5, 6], [7, 8]] +sequence : range 0 4; // [0, 1, 2, 3, 4] +repeated : repeat 3 "x"; // ["x", "x", "x"] +``` + +## Local Bindings with `with` + +Stage local bindings in a function header right after the arrow. Entries are processed left-to-right in an inner scope. You can type locals using the same style as globals. + +```baba +// Untyped locals +addMul : x y -> + with (inc : x + 1; prod : inc * y;) -> + inc + prod; + +// Typed parameters + typed locals +sumNext : (x: Int, y: Int) -> Int -> + with (nx Int; ny Int; nx : x + 1; ny : y + 1;) -> + nx + ny; +``` + +Semicolons +- Inside `with ( ... )`: semicolons separate entries; trailing `;` allowed +- Between header and body: `->` +- After body: same as top-level statements + +## Typed Functions in Practice + +You can annotate parameter and return types. Validation happens at runtime. + +```baba +// Two typed params, typed return +mul : (x: Int, y: Int) -> Int -> x * y; + +// Curried with types +startsWith : (prefix: String, s: String) -> Bool -> str.substring s 0 (str.length prefix) = prefix; + +// Partially applying a typed function +startsWithHello : startsWith "Hello"; +isHello : startsWithHello "Hello, world"; // true +``` + +## Combinators + +Combinators are function building blocks without free variables. + +```baba +// K combinator: K x y = x +K : x y -> x; + +// I combinator: I x = x +I : x -> x; + +// S combinator: S f g x = f x (g x) +S : f g x -> f x (g x); + +// Composition via combinators +compose : f g x -> f (g x); +res : compose (x -> x + 1) (x -> x * 2) 5; // 11 +``` + +## Functions in Tables + +Tables can hold functions; access properties with dot notation. + +```baba +math : { + add: x y -> x + y, + mul: x y -> x * y +}; +resAdd : math.add 2 3; // 5 +``` + +## Advanced Array Programming + +Baba Yaga includes powerful array programming features inspired by APL, K, and Q: + +### Scan Operations (Cumulative Operations) +```baba +// General scan operation +numbers : [1, 2, 3, 4, 5]; +addFunc : acc x -> acc + x; +scanned : scan addFunc 0 numbers; // [0, 1, 3, 6, 10, 15] + +// Built-in utilities +cumsum : cumsum numbers; // [0, 1, 3, 6, 10, 15] +cumprod : cumprod numbers; // [1, 1, 2, 6, 24, 120] +``` + +### Advanced Array Indexing +```baba +data : [10, 21, 30, 43, 50]; + +// Select elements at specific indices +indices : [0, 2, 4]; +selected : at indices data; // [10, 30, 50] + +// Find indices where predicate is true +evenPredicate : x -> x % 2 = 0; +evenIndices : where evenPredicate data; // [0, 2, 4] + +// Take and drop elements +firstThree : take 3 data; // [10, 21, 30] +lastTwo : drop 3 data; // [43, 50] +``` + +### Array Broadcasting Operations +```baba +// Broadcast scalar operation over array +addOp : x y -> x + y; +numbers : [1, 2, 3, 4]; +broadcasted : broadcast addOp 10 numbers; // [11, 12, 13, 14] + +// Element-wise operations between arrays +array1 : [1, 2, 3]; +array2 : [10, 20, 30]; +zipped : zipWith addOp array1 array2; // [11, 22, 33] + +// Reshape arrays into matrices +flatArray : [1, 2, 3, 4, 5, 6]; +matrix : reshape [2, 3] flatArray; // 2x3 matrix +``` + +### Enhanced Function Combinators +```baba +// Flip function arguments +add : x y -> x + y; +flippedAdd : flip add; +result : flippedAdd 3 5; // 8 (same as 5 + 3) + +// Apply function to value +double : x -> x * 2; +result : apply double 7; // 14 + +// Pipe value through function (reverse apply) +result : pipe 5 double; // 10 + +// Function composition +increment : x -> x + 1; +composed : compose increment double; +result : composed 4; // 9 (double then increment) +``` + +### Monadic Operations +```baba +// flatMap for flattening mapped results +duplicateFunc : x -> [x, x]; +original : [1, 2, 3]; +duplicated : flatMap duplicateFunc original; // [1, 1, 2, 2, 3, 3] + +// Chain operations that produce lists +rangeFunc : x -> range 1 x; +chained : flatMap rangeFunc [2, 3]; // [1, 2, 1, 2, 3] +``` + +## Pattern Guards + +Pattern matching can be enhanced with conditional guards using the `if` keyword. For detailed documentation and examples, see [Pattern Matching](./03_pattern-matching.md#pattern-guards). + +```baba +// Example: Age categorization with guards +categorizeAge : age -> + when age is + a if (a >= 0 and a < 18) then "minor" + a if (a >= 18 and a < 65) then "adult" + a if (a >= 65) then "senior" + _ then "invalid"; +``` + +## On Style + +- Prefer small, pure functions +- Build complex behavior by composing simple functions +- Use array programming operations for data transformation +- Leverage pattern guards for complex conditional logic +- Combine scan, broadcast, and flatMap for powerful data processing pipelines diff --git a/js/baba-yaga/docs/02_data-structures.md b/js/baba-yaga/docs/02_data-structures.md new file mode 100644 index 0000000..aa41ed9 --- /dev/null +++ b/js/baba-yaga/docs/02_data-structures.md @@ -0,0 +1,133 @@ +# Data Structures + +Two immutable data structures are built-in, lists and tables. + +## Lists +```baba +nums : [1, 2, 3]; +first : nums.0; // 1 +second : nums.1; // 2 + +// Common operations (immutable) +plus4 : append nums 4; // [1, 2, 3, 4] +head0 : prepend 0 nums; // [0, 1, 2, 3] +joined: concat [1, 2] [3, 4]; // [1, 2, 3, 4] +``` + +## Tables +```baba +user : { name: "Ralph", age: 73 }; +name : user.name; // "Ralph" + +// Functional usage +calculator : { + add: x y -> x + y, + mul: x y -> x * y +}; +res : calculator.add 10 5; // 15 +``` + +## Utilities + +### Core Utilities +```baba +length : Built-in; works on lists and strings +shape : Built-in; returns a metadata table for lists, strings, tables, scalars +``` + +### Array Programming Operations + +Baba Yaga provides powerful array programming operations inspired by APL, K, and Q: + +#### Indexing and Selection +```baba +data : [10, 20, 30, 40, 50]; + +// Select elements at specific indices +selected : at [0, 2, 4] data; // [10, 30, 50] + +// Find indices where predicate is true +evenIndices : where (x -> x % 2 = 0) data; // [0, 1, 2, 3, 4] + +// Take first n elements +firstThree : take 3 data; // [10, 20, 30] + +// Drop first n elements +lastTwo : drop 3 data; // [40, 50] +``` + +#### Cumulative Operations +```baba +numbers : [1, 2, 3, 4, 5]; + +// General scan operation +addFunc : acc x -> acc + x; +scanned : scan addFunc 0 numbers; // [0, 1, 3, 6, 10, 15] + +// Cumulative sum and product utilities +cumSum : cumsum numbers; // [0, 1, 3, 6, 10, 15] +cumProd : cumprod numbers; // [1, 1, 2, 6, 24, 120] +``` + +#### Broadcasting and Element-wise Operations +```baba +values : [1, 2, 3, 4]; + +// Apply scalar operation to each element +addTen : broadcast (x y -> x + y) 10 values; // [11, 12, 13, 14] + +// Element-wise operations on two arrays +array1 : [1, 2, 3]; +array2 : [4, 5, 6]; +multiplied : zipWith (x y -> x * y) array1 array2; // [4, 10, 18] + +// Reshape flat array into matrix +flatData : [1, 2, 3, 4, 5, 6]; +matrix : reshape [2, 3] flatData; // 2x3 matrix +``` + +#### Monadic Operations +```baba +// flatMap for flattening mapped results +duplicator : x -> [x, x]; +original : [1, 2, 3]; +flattened : flatMap duplicator original; // [1, 1, 2, 2, 3, 3] +``` + +### Traditional Data Processing Utilities +```baba +// Array manipulation +numbers : [1, 2, 3, 4, 5, 6]; +grouped : chunk numbers 2; // [[1, 2], [3, 4], [5, 6]] +sequence : range 1 10; // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +duplicated : repeat 4 "item"; // ["item", "item", "item", "item"] + +// Sorting and grouping +people : [{name: "Alice", age: 30}, {name: "Bob", age: 25}]; +sorted : sort.by people (p -> p.age); // Sorted by age +grouped : group.by people (p -> p.age > 27); // Group by age criteria + +// Data validation +valid : validate.notEmpty numbers; // true +inRange : validate.range 1 10 5; // true +correctType : validate.type "List" numbers; // true + +// Text processing +text : "hello world example"; +words : text.words text; // ["hello", "world", "example"] +padded : text.padLeft 15 "centered"; // " centered" +``` + +## Shape +`shape` returns a metadata table describing the argument. It is similar in spirit to APL's shape (⍴) but returns a table with fields. + +```baba +lst : [10, 20, 30]; +sh : shape lst; // { kind: "List", rank: 1, shape: [3], size: 3, isEmpty: false } + +str : "abc"; +shape str; // { kind: "String", rank: 1, shape: [3], size: 3, isEmpty: false } + +tbl : { a: 1, b: 2 }; +shape tbl; // { kind: "Table", rank: 1, shape: [2], size: 2, keys: ["a", "b"], isEmpty: false } +``` diff --git a/js/baba-yaga/docs/03_pattern-matching.md b/js/baba-yaga/docs/03_pattern-matching.md new file mode 100644 index 0000000..0bd663e --- /dev/null +++ b/js/baba-yaga/docs/03_pattern-matching.md @@ -0,0 +1,133 @@ +# Pattern Matching + +The `when` expression matches a discriminant against patterns. + +## Literals and Wildcards +```baba +describe : x -> + when x is + 0 then "Zero" + 1 then "One" + _ then "Other"; +``` + +## Multiple Discriminants +```baba +whereIs : x y -> + when x y is + 0 0 then "Origin" + 1 1 then "Diagonal" + _ _ then "Somewhere"; +``` + +## Type Patterns +```baba +checkType : val -> + when val is + Int then "Integer" + String then "String" + Bool then "Boolean" + _ then "Other"; +``` + +## Pattern Guards + +Use the `if` keyword to add conditional guards to patterns: + +```baba +// Basic guards with range conditions +categorizeNumber : n -> + when n is + x if (x > 0) then "positive" + x if (x < 0) then "negative" + 0 then "zero"; + +// Guards with complex conditions +gradeStudent : score -> + when score is + s if (s >= 90) then "A" + s if (s >= 80 and s < 90) then "B" + s if (s >= 70 and s < 80) then "C" + s if (s >= 60 and s < 70) then "D" + s if (s < 60) then "F" + _ then "Invalid score"; + +// Type guards +processValue : value -> + when value is + Int if value > 100 then "large integer" + Int if value > 0 then "positive integer" + String if (length value) > 10 then "long string" + String if (length value) > 0 then "short string" + _ then "other"; + +// Guards with wildcard patterns +checkRange : x -> + when x is + _ if (x >= 1 and x <= 10) then "small" + _ if (x >= 11 and x <= 100) then "medium" + _ if (x > 100) then "large" + _ then "invalid"; +``` + +Guards are evaluated after the underlying pattern matches. The guard expression has access to any variables bound by the pattern. This allows for sophisticated conditional matching without conflicting with the `..` string concatenation operator. + +## Typed discriminants + +When using typed functions that return `Result`, you can pattern match on variants and bind inner values. + +```baba +divide : (x: Int, y: Int) -> Result -> + when y is + 0 then Err "Division by zero" + _ then Ok (x / y); + +use : r -> + when r is + Ok val then val + Err msg then msg; +``` + +## Result Patterns + +```baba +divide : x y -> + when y is + 0 then Err "Division by zero" + _ then Ok (x / y); + +use : r -> + when r is + Ok val then val + Err msg then msg; +``` + +## Using `with` alongside `when` + +Use `with` to stage named locals used by discriminants and consequents. + +```baba +classify : n -> + with (lo Int; hi Int; lo : 10; hi : 100;) -> + when n is + 0 then "zero" + _ then when (n > hi) is + true then "large" + _ then when (n > lo) is + true then "medium" + _ then "small"; +``` + +## List & Table Patterns + +```baba +is123 : xs -> + when xs is + [1, 2, 3] then true + _ then false; + +hasA1B2 : t -> + when t is + { a: 1, b: 2 } then true + _ then false; +``` diff --git a/js/baba-yaga/docs/04_types.md b/js/baba-yaga/docs/04_types.md new file mode 100644 index 0000000..bf70ef0 --- /dev/null +++ b/js/baba-yaga/docs/04_types.md @@ -0,0 +1,196 @@ +# Type System + +Types are optional. If a type is declared, assignments are checked at runtime. + +## Declaring Types + +```baba +// Type declaration (name Type) +myValue Int; + +// Assignment must match declared type +myValue : 10; // OK (Int) +// myValue : "x"; // Error: expected Int +``` + +## Runtime Type Inference + +Without declarations, values carry runtime type tags used by pattern matching: +- Numbers are tagged as `Int` or `Float`; `Number` is a supertype accepted in validation where numeric values are allowed +- Strings as `String`, Booleans as `Bool` +- Lists and Tables have `List`/`Table` tags + +## Runtime Type Validation Semantics + +- Parameter validation happens at call time for parameters with annotations only. +- Return type validation happens after body evaluation when a return type is declared. +- Untyped parameters are accepted as-is and are not validated. + + +## Function Return Types + +```baba +add : (x: Int, y: Int) -> Int -> x + y; +``` + +## Typed Locals with `with` + +Locals in a header can be typed exactly like globals using a declaration followed by assignment. + +```baba +sumNext : (x: Int, y: Int) -> Int -> + with (nx Int; ny Int; nx : x + 1; ny : y + 1;) -> nx + ny; +``` + +## Numeric Type Lattice and Widening + +The numeric types form a simple lattice: + +``` +Int ⊂ Float ⊂ Number +``` + +- When a parameter expects `Float`, an `Int` is accepted (widened) implicitly. +- When a parameter expects `Number`, either `Int` or `Float` are accepted. +- When a parameter expects `Int`, `Float` is rejected. +- Many `math.` functions return `Float` for predictability. + +## Typed Functions + +Typed functions annotate parameters and the return value. At runtime, arguments and returns are validated. + +### Multi-Parameter Typed Functions + +```baba +// Parameter types and return type +add : (x: Int, y: Int) -> Int -> x + y; + +// Using typed functions +result1 : add 2 3; // OK => 5 +// result2 : add 2 "3"; // Runtime error: parameter type mismatch + +// Multi-parameter function takes all arguments at once +multiply : (x: Float, y: Float) -> Float -> x * y; +result : multiply 2.0 3.0; // 6.0 +``` + +### Curried Typed Functions + +Curried functions take one parameter at a time and return functions for partial application: + +```baba +// Curried function with typed first parameter and function return type +mul : (x: Float) -> (Float -> Float) -> y -> x * y; +double : mul 2.0; // Partial application: double : (Float -> Float) +result : double 3.5; // 7.0 + +// Three-parameter curried function +add3 : (x: Int) -> (Int -> (Int -> Int)) -> y -> z -> x + y + z; +add5 : add3 5; // add5 : (Int -> (Int -> Int)) +add5and3 : add5 3; // add5and3 : (Int -> Int) +result : add5and3 2; // 10 +``` + +### Function Type Syntax + +Function types use the syntax `(ParamType -> ReturnType)`: + +```baba +// Simple function type +transform : (x: Int) -> (Int -> Int) -> y -> x + y; + +// Function that returns another function +makeAdder : (x: Int) -> (Int -> Int) -> y -> x + y; +add5 : makeAdder 5; // add5 : (Int -> Int) +result : add5 3; // 8 +``` +Heads up! Complex nested parameter types like (f: (Int -> Int)) aren't implemented. The first parameter must use simple types like Int, Float, String, or Bool. + +### Mixed Typed/Untyped Functions + +```baba +// Mixed typed/untyped parameters (untyped are not validated) +concatIf : (flag: Bool, x, y) -> String -> + when flag is + true then x .. y + _ then y .. x; + +// Return type enforcement +// This will raise a runtime error if the body evaluates to a non-String +greet : (name: String) -> String -> "Hello " .. name; +``` + +### Backward Compatibility + +All existing function syntax continues to work: + +```baba +// Untyped curried function (existing) +multiply : x -> y -> x * y; + +// Multi-parameter typed function (existing) +add : (x: Float, y: Float) -> Float -> x + y; + +// New: Typed curried function +multiply : (x: Float) -> (Float -> Float) -> y -> x * y; +``` + +## Typed Tables + +Table values carry the `Table` tag and can be typed via variable declarations. Currently, there is no way to create complex types that totally model the typing of the values contained within a table data structure. + +```baba +// Variable type declaration (applies to subsequent assignments) +user Table; +user : { name: "Alice", age: 30 }; + +// Access remains the same +userName : user.name; // "Alice" +userAge : user.age; // 30 +``` + +## Result Type + +`Result` encodes success (`Ok value`) or failure (`Err message`). + +```baba +divide : x y -> + when y is + 0 then Err "Division by zero" + _ then Ok (x / y); + +useDivide : when (divide 10 2) is + Ok val then val + Err _ then 0; +``` +Pattern matching binds inner values: + +```baba +// Function returning a Result +parseIntOrErr : (s: String) -> Result -> + when (str.trim s) is + "" then Err "Empty" + _ then Ok 42; // placeholder + +// Consuming Result +answer : when (parseIntOrErr " 10 ") is + Ok v then v + Err e then 0; +``` + +## Error messages + +All runtime errors are thrown as plain `Error` with messages: + +``` +Type mismatch in function 'add': Expected Int for parameter 'y', but got String (value: "3") +Return type mismatch in function 'greet': Expected String, but got Int (value: 42) +Type mismatch for myValue: expected Int but got String (value: "x") +Division by zero +Index out of bounds: 5 +Undefined variable: foo +Undefined property: age of person +Unknown operator: ^ +Unexpected token: RPAREN ()) at 12:17 +``` +When a location is available, the CLI/REPL shows a code frame indicating `line:column`. diff --git a/js/baba-yaga/docs/05_recursion-and-composition.md b/js/baba-yaga/docs/05_recursion-and-composition.md new file mode 100644 index 0000000..0721916 --- /dev/null +++ b/js/baba-yaga/docs/05_recursion-and-composition.md @@ -0,0 +1,136 @@ +# Recursion and Functional Composition + +This guide shows how to express recursion (including mutual recursion) and how to build programs by composing small functions...probably read the [documentation about functional programming](./01_functional.md) before this. + +## Simple Recursion +```baba +// Factorial +factorial : n -> + when n is + 0 then 1 + 1 then 1 + _ then n * (factorial (n - 1)); + +// Fibonacci +fibonacci : n -> + when n is + 0 then 0 + 1 then 1 + _ then (fibonacci (n - 1)) + (fibonacci (n - 2)); +``` + +## Tail-Recursion with an Accumulator +```baba +// Sum of digits using an accumulator +sumDigits : n acc -> + when n is + 0 then acc + _ then sumDigits (n / 10) (acc + (n % 10)); + +sumDigitsStart : n -> sumDigits n 0; +``` + +## Mutual Recursion +```baba +// Using with rec in a header to define mutually recursive locals +isEvenOdd : z -> with rec ( + isEven : n -> + when n is + 0 then true + _ then isOdd (n - 1); + isOdd : n -> + when n is + 0 then false + _ then isEven (n - 1); +) -> { even: isEven 10, odd: isOdd 7 }; +``` + +## Function Composition + +Baba Yaga provides built-in function combinators for composition. For detailed documentation, see [Functional Programming](./01_functional.md#function-combinators). + +```baba +inc : x -> x + 1; +double : x -> x * 2; + +// Built-in compose (right-to-left): f(g(x)) +composed : compose inc double; +r1 : composed 3; // inc (double 3) = 7 + +// Built-in pipe (left-to-right): value |> function +r2 : pipe 3 inc; // inc 3 = 4 +r3 : pipe 4 double; // double 4 = 8 +``` + +## Composing Many Functions + +You can compose an arbitrary list of unary functions using `reduce`. + +```baba +// composeAll [f, g, h] = x -> f (g (h x)) +composeAll : funcs -> + reduce (acc fn -> (x -> acc (fn x))) (x -> x) funcs; + +inc : x -> x + 1; +double : x -> x * 2; + +combo : composeAll [inc, double]; +res : combo 3; // inc (double 3) = 7 +``` + +## Recursion with Utility Functions + +Using the enhanced utility functions for recursive algorithms: + +```baba +// Recursive data processing with validation +processNumbers : numbers -> + when (validate.notEmpty numbers) is + false then [] + true then + with ( + sorted : sort.by numbers (x -> x); + chunks : chunk sorted 3; + processed : map (chunk -> reduce (acc x -> acc + x) 0 chunk) chunks; + ) -> + processed; + +// Recursive tree traversal with debugging +traverseTree : tree -> + with rec ( + // Debug each node we visit + visitNode : node -> + when (validate.notEmpty (keys node)) is + false then (debug.print "Empty node"; 0) + true then + with (value : node.value;) -> + (debug.print "Visiting" value; value); + + // Recursive traversal + traverse : node -> + when (validate.notEmpty (keys node)) is + false then 0 + true then + (visitNode node) + + (traverse node.left) + + (traverse node.right); + ) -> + traverse tree; + +// Generate and process sequences recursively +fibonacci : n -> + when (validate.range 0 100 n) is + false then (assert false "n must be 0-100"; 0) + true then + when n is + 0 then 0 + 1 then 1 + _ then (fibonacci (n - 1)) + (fibonacci (n - 2)); + +// Generate fibonacci sequence using range and recursion +fibSequence : count -> + with (indices : range 0 (count - 1);) -> + map fibonacci indices; + +// Example: fibSequence 10 generates [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] +``` \ No newline at end of file diff --git a/js/baba-yaga/docs/06_error-handling.md b/js/baba-yaga/docs/06_error-handling.md new file mode 100644 index 0000000..7bc2a78 --- /dev/null +++ b/js/baba-yaga/docs/06_error-handling.md @@ -0,0 +1,632 @@ +# Error Handling + +Baba Yaga takes a functional approach to error handling, emphasizing explicit error representation and fail-fast programming practices. Instead of exceptions, the language uses the `Result` type for recoverable errors and assertions for programming errors. + +## Philosophy + +- **No Exceptions**: All errors are values that must be handled explicitly +- **Result Type**: Use `Ok`/`Err` variants for operations that might fail +- **Assertions**: Use `assert` for programming errors that should never happen +- **Validation First**: Validate inputs early with the `validate.*` namespace +- **Rich Debugging**: Use `debug.*` tools for development and troubleshooting + +## The Result Type + +The `Result` type represents either success (`Ok value`) or failure (`Err message`). This forces explicit handling of potential failures. + +### Basic Result Usage + +```baba +// Function that might fail +divide : x y -> + when y is + 0 then Err "Division by zero" + _ then Ok (x / y); + +// Using Result with pattern matching +handleDivision : x y -> + when (divide x y) is + Ok result then result + Err message then 0; + +result1 : handleDivision 10 2; // 5 +result2 : handleDivision 10 0; // Returns 0 +``` + +### Result Type Patterns + +```baba +// Parsing with Result +parsePositiveInt : str -> + when str is + "0" then Err "Zero is not positive" + "1" then Ok 1 + "2" then Ok 2 + "5" then Ok 5 + "10" then Ok 10 + _ then Err "Invalid or unsupported number"; + +// Chaining Result operations +processNumber : input -> + when (parsePositiveInt input) is + Err msg then Err msg + Ok num then + when (num > 100) is + true then Err "Number too large" + false then Ok (num * 2); + +// Usage examples +result1 : processNumber "5"; // Ok 10 +result2 : processNumber "0"; // Err "Zero is not positive" +result3 : processNumber "200"; // Err "Number too large" +``` + +## Input Validation + +Use the `validate.*` namespace to check inputs early and prevent errors downstream. + +### Validation Patterns + +```baba +// Basic validation +validateUserInput : input -> + when (validate.notEmpty input) is + false then Err "Input cannot be empty" + true then + when (validate.type "String" input) is + false then Err "Input must be a string" + true then Ok input; + +// Multiple validation checks +validateAge : age -> + when (validate.type "Int" age) is + false then Err "Age must be an integer" + true then + when (validate.range 0 150 age) is + false then Err "Age must be between 0 and 150" + true then Ok age; + +// Email validation +validateEmail : email -> + when (validate.notEmpty email) is + false then Err "Email cannot be empty" + true then + when (validate.email email) is + false then Err "Invalid email format" + true then Ok email; +``` + +### Combining Validations + +```baba +// Validate user registration data +validateUser : userData -> + with ( + nameResult : validateUserInput userData.name; + emailResult : validateEmail userData.email; + ageResult : validateAge userData.age; + ) -> + when nameResult is + Err msg then Err ("Name error: " .. msg) + Ok name then + when emailResult is + Err msg then Err ("Email error: " .. msg) + Ok email then + when ageResult is + Err msg then Err ("Age error: " .. msg) + Ok age then Ok {name: name, email: email, age: age}; + +// Usage +validUser : validateUser {name: "Alice", email: "alice@example.com", age: 25}; +invalidUser : validateUser {name: "", email: "bad-email", age: 200}; +``` + +## Error Chaining and Propagation + +Handle sequences of operations that might fail at any step. + +### Sequential Operations + +```baba +// Chain of operations that might fail +processUserData : rawData -> + when (validateUser rawData) is + Err msg then Err ("Validation failed: " .. msg) + Ok user then + when (checkUserExists user.email) is + Err msg then Err ("User check failed: " .. msg) + Ok exists then + when exists is + true then Err "User already exists" + false then + when (saveUser user) is + Err msg then Err ("Save failed: " .. msg) + Ok savedUser then Ok savedUser; + +// Simulated helper functions +checkUserExists : email -> + when email is + "admin@example.com" then Ok true + _ then Ok false; + +saveUser : user -> + when (validate.notEmpty user.name) is + false then Err "Cannot save user with empty name" + true then Ok user; +``` + +### Error Recovery Strategies + +```baba +// Try multiple approaches +parseNumberWithFallback : input -> + when (parsePositiveInt input) is + Ok num then Ok num + Err _ then + when input is + "zero" then Ok 0 + "one" then Ok 1 + "two" then Ok 2 + _ then Err "Could not parse number"; + +// Provide default values +getConfigValue : key defaultValue -> + when (loadConfig key) is + Ok value then value + Err _ then defaultValue; + +// Simulated config loader +loadConfig : key -> + when key is + "timeout" then Ok 30 + "retries" then Ok 3 + _ then Err "Config key not found"; +``` + +## Assertions vs Results + +Use `assert` for programming errors (bugs) and `Result` for expected failures. + +### When to Use Assert + +```baba +// Programming errors - should never happen in correct code +calculateArea : width height -> + // Assert preconditions + assert (width > 0) "Width must be positive"; + assert (height > 0) "Height must be positive"; + assert (validate.type "Number" width) "Width must be a number"; + assert (validate.type "Number" height) "Height must be a number"; + + width * height; + +// Array bounds checking +getElement : list index -> + assert (validate.type "List" list) "First argument must be a list"; + assert (validate.type "Int" index) "Index must be an integer"; + assert (index >= 0) "Index must be non-negative"; + assert (index < (length list)) "Index out of bounds"; + + list.index; +``` + +### When to Use Result + +```baba +// Expected failures - user input, external resources, business logic +safeGetElement : list index -> + when (validate.type "List" list) is + false then Err "Not a list" + true then + when (validate.type "Int" index) is + false then Err "Index must be integer" + true then + when (validate.range 0 ((length list) - 1) index) is + false then Err "Index out of bounds" + true then Ok list.index; + +// File operations (simulated) +readUserFile : filename -> + when (validate.notEmpty filename) is + false then Err "Filename cannot be empty" + true then + when filename is + "config.txt" then Ok "timeout=30,retries=3" + "users.json" then Ok "Alice" + _ then Err ("File not found: " .. filename); +``` + +## Debugging and Development + +Use the `debug.*` namespace for troubleshooting and development. + +### Debug Printing + +```baba +// Debug intermediate values in error-prone operations +complexCalculation : input -> + with ( + step1 : input * 2; + _ : debug.print "Step1" step1; + step2 : step1 + 10; + _ : debug.print "Step2" step2; + result : step2 / 3; + _ : debug.print "Final" result; + ) -> + result; + +// Debug error paths +parseWithDebug : input -> + debug.print "Parsing input" input; + when (validate.notEmpty input) is + false then + with (_ : debug.print "Empty input detected";) -> + Err "Empty input" + true then + when (parsePositiveInt input) is + Err msg then + with (_ : debug.print "Parse error" msg;) -> + Err msg + Ok result then + with (_ : debug.print "Parse success" result;) -> + Ok result; +``` + +### Value Inspection + +```baba +// Inspect complex data structures during debugging +analyzeUserData : userData -> + with ( + inspection : debug.inspect userData; + _ : debug.print "User data structure:"; + _ : debug.print inspection; + + validation : validateUser userData; + _ : debug.print "Validation result" validation; + ) -> + validation; + +// Debug function behavior +debugFunction : fn input -> + with ( + fnInfo : debug.inspect fn; + _ : debug.print "Function info" fnInfo; + _ : debug.print "Input" input; + + result : fn input; + _ : debug.print "Result" result; + ) -> + result; +``` + +## Real-World Error Handling Patterns + +### Game State Validation + +```baba +// Validate game state for consistency +validateGameState : state -> + with ( + playerValid : when (validate.range 0 100 state.playerHealth) is + false then Err "Player health out of range" + true then Ok state.playerHealth; + + levelValid : when (validate.range 1 10 state.currentLevel) is + false then Err "Invalid level" + true then Ok state.currentLevel; + + inventoryValid : when (validate.notEmpty state.inventory) is + false then Err "Inventory cannot be empty" + true then Ok state.inventory; + ) -> + when playerValid is + Err msg then Err msg + Ok _ then + when levelValid is + Err msg then Err msg + Ok _ then + when inventoryValid is + Err msg then Err msg + Ok _ then Ok state; + +// Game action with error handling +performAction : gameState action -> + when (validateGameState gameState) is + Err msg then Err ("Invalid game state: " .. msg) + Ok validState then + when action is + "heal" then + when (validState.playerHealth < 100) is + false then Err "Player already at full health" + true then Ok (set validState "playerHealth" 100) + "attack" then + when (length validState.inventory > 0) is + false then Err "No weapons available" + true then Ok (set validState "playerHealth" (validState.playerHealth - 10)) + _ then Err ("Unknown action: " .. action); +``` + +### Data Processing Pipeline + +```baba +// Process data through multiple stages with error handling +processDataPipeline : rawData -> + when (validate.notEmpty rawData) is + false then Err "No data to process" + true then + when (cleanData rawData) is + Err msg then Err ("Cleaning failed: " .. msg) + Ok cleaned then + when (transformData cleaned) is + Err msg then Err ("Transform failed: " .. msg) + Ok transformed then + when (validateOutput transformed) is + Err msg then Err ("Validation failed: " .. msg) + Ok validated then Ok validated; + +// Simulated pipeline stages +cleanData : data -> + when (validate.type "List" data) is + false then Err "Data must be a list" + true then + with (filtered : filter (x -> validate.notEmpty x) data;) -> + when (validate.notEmpty filtered) is + false then Err "No valid data after cleaning" + true then Ok filtered; + +transformData : data -> + when (validate.notEmpty data) is + false then Err "Cannot transform empty data" + true then Ok (map (x -> x * 2) data); + +validateOutput : data -> + when (length data < 1) is + true then Err "Output too small" + false then + when (length data > 1000) is + true then Err "Output too large" + false then Ok data; +``` + +### Configuration Loading + +```baba +// Load and validate configuration with fallbacks +loadConfiguration : configFile -> + when (readUserFile configFile) is + Err msg then + debug.print "Config load failed, using defaults" msg; + Ok {timeout: 30, retries: 3, debug: false} + Ok content then + when (parseConfig content) is + Err msg then + debug.print "Config parse failed, using defaults" msg; + Ok {timeout: 30, retries: 3, debug: false} + Ok config then + when (validateConfig config) is + Err msg then Err ("Invalid config: " .. msg) + Ok validConfig then Ok validConfig; + +// Simulated config parsing +parseConfig : content -> + when content is + "timeout=30,retries=3" then Ok {timeout: 30, retries: 3, debug: false} + "timeout=60,retries=5,debug=true" then Ok {timeout: 60, retries: 5, debug: true} + _ then Err "Unrecognized config format"; + +validateConfig : config -> + when (validate.range 1 300 config.timeout) is + false then Err "Timeout must be 1-300 seconds" + true then + when (validate.range 1 10 config.retries) is + false then Err "Retries must be 1-10" + true then Ok config; +``` + +## Error Handling Best Practices + +### 1. Fail Fast with Validation + +```baba +// Good: Validate early +processUser : userData -> + when (validateUser userData) is + Err msg then Err msg + Ok user then expensiveOperation user; + +// Avoid: Validate late +// processUser : userData -> +// result : expensiveOperation userData; +// when (validateUser userData) is +// Err msg then Err msg +// Ok _ then result; +``` + +### 2. Provide Meaningful Error Messages + +```baba +// Good: Specific error messages +validatePassword : password -> + when (validate.notEmpty password) is + false then Err "Password cannot be empty" + true then + when (str.length password < 8) is + true then Err "Password must be at least 8 characters" + false then + when (validate.email password) is + true then Err "Password cannot be an email address" + false then Ok password; + +// Avoid: Generic error messages +// validatePassword : password -> +// when (someValidation password) is +// false then Err "Invalid password" +// true then Ok password; +``` + +### 3. Use Assertions for Programming Errors + +```baba +// Good: Assert impossible conditions +fibonacci : n -> + assert (n >= 0) "Fibonacci input must be non-negative"; + when n is + 0 then 0 + 1 then 1 + _ then (fibonacci (n - 1)) + (fibonacci (n - 2)); + +// Good: Use Result for user errors +safeFibonacci : n -> + when (validate.type "Int" n) is + false then Err "Input must be an integer" + true then + when (validate.range 0 40 n) is + false then Err "Input must be between 0 and 40" + true then Ok (fibonacci n); +``` + +### 4. Debug Complex Error Flows + +```baba +// Use debug.print to trace error paths +complexValidation : data -> + debug.print "Starting validation" data; + + when (validate.notEmpty data) is + false then + with (_ : debug.print "Failed: empty data";) -> + Err "Empty data" + true then + with (_ : debug.print "Passed: not empty";) -> + when (validate.type "List" data) is + false then + with (_ : debug.print "Failed: not a list";) -> + Err "Must be list" + true then + with (_ : debug.print "Passed: is list";) -> + when ((length data) > 100) is + true then + with (_ : debug.print "Failed: too large";) -> + Err "Too large" + false then + with (_ : debug.print "Success: validation complete";) -> + Ok data; +``` + +### 5. Compose Error Handling + +```baba +// Create reusable error handling combinators +mapResult : fn result -> + when result is + Err msg then Err msg + Ok value then Ok (fn value); + +chainResult : fn result -> + when result is + Err msg then Err msg + Ok value then fn value; + +// Compose operations by nesting function calls +processUserChain : userData -> + mapResult saveUser + (chainResult checkUserExists + (chainResult validateUser + (Ok userData))); + +// Or use intermediate variables for clarity +processUserStep : userData -> + with ( + step1 : chainResult validateUser (Ok userData); + step2 : chainResult checkUserExists step1; + step3 : mapResult saveUser step2; + ) -> + step3; +``` + +## Summary + +Baba Yaga's error handling approach emphasizes: + +1. **Explicit Error Values**: Use `Result` type instead of exceptions +2. **Early Validation**: Check inputs with `validate.*` functions +3. **Clear Distinction**: `assert` for bugs, `Result` for expected failures +4. **Rich Debugging**: Use `debug.*` tools during development +5. **Meaningful Messages**: Provide specific, actionable error information +6. **Composition**: Chain operations while preserving error information + +This approach leads to more robust, predictable code where error handling is an explicit part of the program's logic rather than an afterthought. + +## Array Programming Error Handling + +Array programming operations include specific error cases that should be handled appropriately: + +### Index Bounds Errors +```baba +// Safe array access with bounds checking +safeAt : indices data -> + with ( + validIndices : filter (i -> i >= 0 and i < length data) indices; + ) -> when (length validIndices = length indices) is + true then Ok (at validIndices data) + _ then Err "One or more indices out of bounds"; + +// Usage +data : [1, 2, 3]; +result1 : safeAt [0, 2] data; // Ok [1, 3] +result2 : safeAt [0, 5] data; // Err "One or more indices out of bounds" +``` + +### Reshape Dimension Errors +```baba +// Safe reshape with dimension validation +safeReshape : dimensions flatArray -> + with ( + totalElements : reduce (acc x -> acc * x) 1 dimensions; + arrayLength : length flatArray; + ) -> when (totalElements = arrayLength) is + true then Ok (reshape dimensions flatArray) + _ then Err ("Cannot reshape array of length " .. arrayLength .. " to dimensions " .. dimensions); + +// Usage +data : [1, 2, 3, 4, 5, 6]; +result1 : safeReshape [2, 3] data; // Ok (2x3 matrix) +result2 : safeReshape [2, 4] data; // Err "Cannot reshape array of length 6 to dimensions [2, 4]" +``` + +### Function Type Validation +```baba +// Validate function arguments for array operations +safeScan : func initial array -> + when func is + Function then Ok (scan func initial array) + _ then Err "scan expects a function as first argument"; + +// Usage +addFunc : acc x -> acc + x; +numbers : [1, 2, 3]; +result1 : safeScan addFunc 0 numbers; // Ok [0, 1, 3, 6] +result2 : safeScan 42 0 numbers; // Err "scan expects a function as first argument" +``` + +### Negative Count Validation +```baba +// Safe take/drop with non-negative validation +safeTake : n array -> + when n is + count if (count >= 0) then Ok (take count array) + _ then Err "take expects a non-negative number"; + +safeDrop : n array -> + when n is + count if (count >= 0) then Ok (drop count array) + _ then Err "drop expects a non-negative number"; + +// Usage +data : [1, 2, 3, 4, 5]; +result1 : safeTake 3 data; // Ok [1, 2, 3] +result2 : safeTake -1 data; // Err "take expects a non-negative number" +``` + +These patterns demonstrate how to wrap array programming operations in safe functions that return `Result` types, allowing graceful error handling in data processing pipelines. diff --git a/js/baba-yaga/docs/07_gotchyas.md b/js/baba-yaga/docs/07_gotchyas.md new file mode 100644 index 0000000..dc71b38 --- /dev/null +++ b/js/baba-yaga/docs/07_gotchyas.md @@ -0,0 +1,642 @@ +# Baba Yaga Syntax Gotchas + +This document catalogs the strict syntax requirements and common pitfalls discovered during development and testing of the `with` and `with rec` functionality. + +## Table of Contents + +1. [When Expression Syntax](#when-expression-syntax) +2. [With Block Syntax](#with-block-syntax) +3. [Operator Precedence and Parenthesization Rules](#operator-precedence-and-parenthesization-rules) +4. [Type System Requirements](#type-system-requirements) +5. [Function Definitions](#function-definitions) +6. [Data Structure Syntax](#data-structure-syntax) +7. [Common Error Patterns](#common-error-patterns) +8. [JavaScript Interop Gotchas](#javascript-interop-gotchas) + +## When Expression Syntax + +### **Incorrect: Using `else` keyword** +```baba +// WRONG - Baba Yaga doesn't use 'else' +status : when x is + 0 then "zero" + _ else "other"; +``` + +### **Correct: Using `_ then` pattern** +```baba +// CORRECT - Use '_ then' for fallback +status : when x is + 0 then "zero" + _ then "other"; +``` + +### **Incorrect: Nested when without proper structure** +```baba +// WRONG - Missing 'then when' introduction +status : when x is + 0 then "zero" + _ when x < 10 then "small" // Missing 'then when' + _ then "large"; +``` + +### **Correct: Proper nested when structure** +```baba +// CORRECT - Use 'then when' for nested conditions +status : when x is + 0 then "zero" + _ then when (x < 10) is + true then "small" + _ then "large"; +``` + +### **Incorrect: Complex when without parentheses** +```baba +// WRONG - Missing parentheses around complex conditions +status : when x > 0 and x < 10 is + true then "small" + _ then "large"; +``` + +### **Correct: Parentheses around complex conditions** +```baba +// CORRECT - Wrap complex conditions in parentheses +status : when (x > 0 and x < 10) is + true then "small" + _ then "large"; +``` + +## With Block Syntax + +### **Incorrect: Missing semicolons** +```baba +// WRONG - Missing semicolon after when expression +with ( + status : when x > 0 is true then "positive" _ then "negative" // Missing ; +) -> status; +``` + +### **Correct: Proper semicolon usage** +```baba +// CORRECT - Semicolon after each entry +with ( + status : when x > 0 is true then "positive" _ then "negative"; +) -> status; +``` + +### **Incorrect: with rec with non-function bindings** +```baba +// WRONG - with rec only allows function bindings +with rec ( + x : 5; // Not a function! + f : y -> y + 1; +) -> f x; +``` + +### **Correct: with rec with function bindings only** +```baba +// CORRECT - All bindings must be functions +with rec ( + f : x -> x + 1; + g : y -> y * 2; +) -> f (g 5); +``` + +**Important**: `with rec` is **strictly function-only**. It does not allow any non-function bindings, even when functions are also present. This is by design to ensure mutual recursion works correctly. For non-function bindings, use regular `with` blocks instead. + +### **Incorrect: Complex list literals with when expressions** +```baba +// WRONG - Complex when expressions in list literals +tags : [name, when age >= 18 is true then "adult" _ then "minor"]; +``` + +### **Correct: Pre-compute values before list creation** +```baba +// CORRECT - Compute values first, then create list +with ( + ageGroup : when age >= 18 is true then "adult" _ then "minor"; + tags : [name, ageGroup]; +) -> tags; +``` + +## Type System Requirements + +### **Incorrect: Function type annotations** +```baba +// WRONG - Function type annotations not supported +factorial : (x: Int) -> Int -> x + 1; +``` + +### **Correct: Simple type declarations** +```baba +// CORRECT - Use simple type declarations +factorial : x -> x + 1; +``` + +**Note**: While Baba Yaga supports function type annotations in function signatures (e.g., `add : (x: Int, y: Int) -> Int -> x + y;`), it does NOT support function type annotations in `with` blocks. See [Types Documentation](./04_types.md) for details on supported type annotations. + +### **Incorrect: Non-existent functions** +```baba +// WRONG - These functions don't exist in Baba Yaga +str.toString x; // No str.toString function +math.floor x; // No math.floor function +type x; // No type function +null; // No null value +``` + +### **Correct: Use existing functions** +```baba +// CORRECT - Use documented functions +str.upper x; // str.upper exists +math.sqrt x; // math.sqrt exists +math.abs x; // math.abs exists +``` + +**Note**: For a complete list of available functions, see the [Types Documentation](./04_types.md) and [Crash Course](./00_crash-course.md). Baba Yaga has a focused set of built-in functions rather than trying to replicate all JavaScript functionality. + +## Function Definitions + +### **Incorrect: Missing arrow syntax** +```baba +// WRONG - Missing -> arrow +f x { x + 1 } +``` + +### **Correct: Proper arrow syntax** +```baba +// CORRECT - Use -> arrow +f : x -> x + 1; +``` + +### **Incorrect: Missing semicolon after function body** +```baba +// WRONG - Missing semicolon +f : x -> x + 1 // Missing ; +``` + +### **Correct: Proper semicolon usage** +```baba +// CORRECT - Semicolon after function definition +f : x -> x + 1; +``` + +## Operator Precedence and Parenthesization Rules + +Baba Yaga has strict requirements for operator precedence that differ from many other languages. Understanding these rules is crucial for writing correct code. + +### **Operator Precedence Hierarchy (Highest to Lowest)** + +1. **Function Calls and Member Access** - `f x`, `obj.property` +2. **Unary Operators** - `-x`, `!x` +3. **Arithmetic** - `*`, `/`, `%` (left-associative) +4. **Arithmetic** - `+`, `-` (left-associative) +5. **Comparison** - `=`, `!=`, `<`, `<=`, `>`, `>=` (non-associative) +6. **Logical** - `and`, `or` (left-associative) + +### **Critical Rule: Function Calls in Comparisons** + +**Incorrect: Function calls without parentheses in comparisons** +```baba +// WRONG - Function calls need parentheses in comparisons +length list > 5; // Error: Unexpected token: OPERATOR (>) +math.abs x < 0.001; // Error: Unexpected token: OPERATOR (<) +str.length input >= 10; // Error: Unexpected token: OPERATOR (>=) +``` + +**Correct: Function calls wrapped in parentheses** +```baba +// CORRECT - Wrap function calls in parentheses +(length list) > 5; // ✓ Works correctly +(math.abs x) < 0.001; // ✓ Works correctly +(str.length input) >= 10; // ✓ Works correctly +``` + +### **Critical Rule: Logical Operator Precedence** + +**Incorrect: Logical operators without parentheses** +```baba +// WRONG - Logical operators need parentheses +a > 0 and b > 0; // Error: Unexpected token: KEYWORD (and) +x = y or y = z; // Error: Unexpected token: KEYWORD (or) +isValid and hasData; // Error: Unexpected token: KEYWORD (and) +``` + +**Correct: Logical operators wrapped in parentheses** +```baba +// CORRECT - Wrap logical expressions in parentheses +(a > 0) and (b > 0); // ✓ Works correctly +(x = y) or (y = z); // ✓ Works correctly +(isValid) and (hasData); // ✓ Works correctly +``` + +### **Critical Rule: Complex Comparison Chains** + +**Incorrect: Complex comparisons without parentheses** +```baba +// WRONG - Complex comparisons need parentheses +math.abs (x * x + y * y) - (z * z) < 0.001; // Error: Unexpected token: OPERATOR (-) +side1 + side2 > side3; // Error: Unexpected token: OPERATOR (>) +``` + +**Correct: Complex comparisons wrapped in parentheses** +```baba +// CORRECT - Wrap complex comparisons in parentheses +(math.abs ((x * x + y * y) - (z * z))) < 0.001; // ✓ Works correctly +((side1 + side2) > side3); // ✓ Works correctly +``` + +### **Automation Rules for Future Tooling** + +For future syntax correction tools, apply these rules in order: + +#### **Rule 1: Function Call Wrapping** +- **Pattern**: `function_name args OPERATOR value` +- **Action**: Wrap function call: `(function_name args) OPERATOR value` +- **Examples**: + - `length list > 0` → `(length list) > 0` + - `math.sqrt x < 10` → `(math.sqrt x) < 10` + - `str.trim input >= 5` → `(str.trim input) >= 5` + +#### **Rule 2: Logical Operator Wrapping** +- **Pattern**: `expression1 and expression2` or `expression1 or expression2` +- **Action**: Wrap each expression: `(expression1) and (expression2)` +- **Examples**: + - `a > 0 and b > 0` → `(a > 0) and (b > 0)` + - `x = y or y = z` → `(x = y) or (y = z)` + - `isValid and hasData and isReady` → `(isValid) and (hasData) and (isReady)` + +#### **Rule 3: Complex Expression Wrapping** +- **Pattern**: `arithmetic_expression OPERATOR value` +- **Action**: Wrap arithmetic expression: `(arithmetic_expression) OPERATOR value` +- **Examples**: + - `x + y > z` → `(x + y) > z` + - `a * b + c <= d` → `(a * b + c) <= d` + - `math.abs (x - y) < 0.001` → `(math.abs (x - y)) < 0.001` + +#### **Rule 4: Nested Function Call Wrapping** +- **Pattern**: `function_call (args) OPERATOR value` +- **Action**: Wrap entire function call: `(function_call (args)) OPERATOR value` +- **Examples**: + - `math.abs (x * x + y * y) < 0.001` → `(math.abs (x * x + y * y)) < 0.001` + - `str.length (str.trim input) >= 5` → `(str.length (str.trim input)) >= 5` + +### **Common Patterns That Always Need Parentheses** + +```baba +// These patterns ALWAYS need parentheses: + +// 1. Function calls in comparisons +(length list) > 0; +(math.sqrt x) < 10; +(str.trim input) >= 5; + +// 2. Logical combinations +(a > 0) and (b > 0); +(x = y) or (y = z); +(isValid) and (hasData); + +// 3. Complex arithmetic in comparisons +((a + b) > c); +((x * y + z) <= 100); +((math.abs (x - y)) < 0.001); + +// 4. Nested function calls in comparisons +((math.abs (x * x + y * y)) < 0.001); +((str.length (str.trim input)) >= 10); +``` + +### **Why These Rules Exist** + +Baba Yaga's parser is designed for clarity and explicit precedence. Unlike languages that use operator precedence rules, Baba Yaga requires explicit parentheses to: + +1. **Eliminate ambiguity** - No guessing about operator precedence +2. **Improve readability** - Intent is always clear +3. **Prevent errors** - Compile-time detection of precedence issues +4. **Enable parsing** - Simpler, more predictable parsing logic + +### **Regex Patterns for Automated Correction** + +For future tooling, these regex patterns can identify and fix common parenthesization issues: + +#### **Pattern 1: Function Calls in Comparisons** +```regex +# Find: function_name args OPERATOR value +(\w+(?:\.\w+)?(?:\s+[^><=!]+)*)\s*([><=!]=?)\s*([^;,\s]+) + +# Replace: (function_name args) OPERATOR value +($1) $2 $3 + +# Examples: +# length list > 0 → (length list) > 0 +# math.abs x < 0.001 → (math.abs x) < 0.001 +# str.trim input >= 5 → (str.trim input) >= 5 +``` + +#### **Pattern 2: Logical Operators** +```regex +# Find: expression1 and expression2 +([^;\s]+)\s+(and|or)\s+([^;\s]+) + +# Replace: (expression1) and (expression2) +($1) $2 ($3) + +# Examples: +# a > 0 and b > 0 → (a > 0) and (b > 0) +# x = y or y = z → (x = y) or (y = z) +``` + +#### **Pattern 3: Complex Arithmetic in Comparisons** +```regex +# Find: arithmetic_expression OPERATOR value +([^;\s]*[\+\-\*\/][^;\s]*)\s*([><=!]=?)\s*([^;,\s]+) + +# Replace: (arithmetic_expression) OPERATOR value +($1) $2 $3 + +# Examples: +# x + y > z → (x + y) > z +# a * b + c <= d → (a * b + c) <= d +``` + +#### **Pattern 4: Nested Function Calls in Comparisons** +```regex +# Find: function_call (args) OPERATOR value +(\w+(?:\.\w+)?\s*\([^)]+\))\s*([><=!]=?)\s*([^;,\s]+) + +# Replace: (function_call (args)) OPERATOR value +($1) $2 $3 + +# Examples: +# math.abs (x - y) < 0.001 → (math.abs (x - y)) < 0.001 +# str.length (str.trim input) >= 5 → (str.length (str.trim input)) >= 5 +``` + +### **Automated Correction Algorithm** + +```python +def fix_baba_yaga_syntax(code): + """ + Apply parenthesization rules to Baba Yaga code. + Apply rules in order to avoid conflicts. + """ + + # Rule 1: Fix function calls in comparisons + code = re.sub( + r'(\w+(?:\.\w+)?(?:\s+[^><=!]+)*)\s*([><=!]=?)\s*([^;,\s]+)', + r'(\1) \2 \3', + code + ) + + # Rule 2: Fix logical operators + code = re.sub( + r'([^;\s]+)\s+(and|or)\s+([^;\s]+)', + r'(\1) \2 (\3)', + code + ) + + # Rule 3: Fix complex arithmetic in comparisons + code = re.sub( + r'([^;\s]*[\+\-\*\/][^;\s]*)\s*([><=!]=?)\s*([^;,\s]+)', + r'(\1) \2 \3', + code + ) + + # Rule 4: Fix nested function calls in comparisons + code = re.sub( + r'(\w+(?:\.\w+)?\s*\([^)]+\))\s*([><=!]=?)\s*([^;,\s]+)', + r'(\1) \2 \3', + code + ) + + return code +``` + +### **Validation Rules** + +After applying corrections, validate that: + +1. **All comparisons have balanced parentheses** +2. **Logical operators are properly wrapped** +3. **Function calls in comparisons are wrapped** +4. **No syntax errors remain** + +### **Edge Cases and Limitations** + +- **Nested parentheses**: May require multiple passes +- **Complex expressions**: May need manual review +- **String literals**: Avoid modifying content inside quotes +- **Comments**: Preserve comment formatting +- **Line breaks**: Handle multi-line expressions carefully + +## Type System Gotchas + +### **Incorrect: Expecting JavaScript-like type behavior** +```baba +// WRONG - Baba Yaga has different type semantics +x : null; // No null value +x : undefined; // No undefined +x : NaN; // No NaN +``` + +### **Correct: Use Baba Yaga type system** +```baba +// CORRECT - Use Result type for optional values +x : Ok 5; // Success case +x : Err "error"; // Error case + +// CORRECT - Use when expressions for conditional logic +x : when (y > 0) is true then y _ then 0; +``` + +### **Incorrect: Ignoring type widening rules** +```baba +// WRONG - May cause type errors +floatVal Float; floatVal : 3.14; // Float literal +intVal Int; intVal : floatVal; // Error: Float cannot be assigned to Int +``` + +### **Correct: Follow type widening hierarchy** +```baba +// CORRECT - Int ⊂ Float ⊂ Number +intVal Int; intVal : 5; +floatVal Float; floatVal : intVal; // Int → Float ✓ +numberVal Number; numberVal : floatVal; // Float → Number ✓ +``` + +## Data Structure Syntax + +### **Incorrect: Table literal shorthand** +```baba +// WRONG - Baba Yaga requires key: value pairs +{ sum, product, difference } // Missing colons +``` + +### **Correct: Explicit key-value pairs** +```baba +// CORRECT - Explicit key: value syntax +{ sum: sum, product: product, difference: difference } +``` + +### **Incorrect: Dynamic property access** +```baba +// WRONG - Dynamic indexing not supported +items.(count - 1) // Unsupported property access +``` + +### **Correct: Use when expressions for conditional access** +```baba +// CORRECT - Use when for conditional access +with ( + item : when count is + 1 then items.0 + _ then when count is + 2 then items.1 + _ then items.2; +) -> item; +``` + +### **Incorrect: Expecting JavaScript-like object access** +```baba +// WRONG - Baba Yaga objects have different structure +result.property; // May not work as expected +result['property']; // Not supported +``` + +### **Correct: Use Baba Yaga object access patterns** +```baba +// CORRECT - Baba Yaga objects use properties Map +result.properties.get('property'); + +// CORRECT - For table literals, use dot notation +{ name: "John", age: 30 }.name; // "John" +``` + +## Cross-References + +For comprehensive information about Baba Yaga's type system, see: +- **[Types Documentation](./04_types.md)** - Complete type system reference +- **[Recursion Documentation](./05_recursion-and-composition.md)** - Details on `with rec` usage +- **[Crash Course](./00_crash-course.md)** - Examples and patterns +- **[JavaScript Interop](./09_js-interop.md)** - Complete JS interop reference + +## Common Error Patterns + +### 1. **Unexpected SEMICOLON errors** +- **Cause**: Missing semicolon after `when` expressions in `with` blocks +- **Solution**: Always add semicolon after each `with` block entry + +### 2. **Unexpected COLON errors** +- **Cause**: Incorrect table literal syntax +- **Solution**: Use `{ key: value }` not `{ key, value }` + +### 3. **Unexpected KEYWORD errors** +- **Cause**: Incorrect when expression structure +- **Solution**: Use `_ then when (condition) is` pattern + +### 4. **Unexpected RBRACKET errors** +- **Cause**: Complex expressions in list literals +- **Solution**: Pre-compute values before creating lists + +### 5. **"with rec expects function-valued bindings" errors** +- **Cause**: Non-function bindings in with rec +- **Solution**: Only use function bindings in with rec + +### 6. **"Undefined property" errors** +- **Cause**: Using non-existent functions +- **Solution**: Check documentation for available functions + +## Best Practices + +### 1. **When Expressions** +- Always use `_ then` for fallback cases +- Use `then when` for nested conditions +- Wrap complex conditions in parentheses +- Add semicolons after when expressions in `with` blocks + +### 2. **With vs With Rec** +- **Use `with`** for: + - Simple local bindings + - Computed values + - Mixed types (functions, values, expressions) + - Non-recursive local functions + +- **Use `with rec`** for: + - Mutually recursive functions only + - When local functions need to reference each other + - Never for non-function bindings + +### 3. **With Blocks** +- Add semicolons after each entry +- Use with rec only for mutually recursive functions +- Pre-compute complex values before using in data structures + +### 4. **Type System** +- Use simple type declarations: `x Int; x : 5;` +- Avoid function type annotations + +### 5. **Data Structures** +- Use explicit `key: value` pairs in tables +- Use `when` expressions for conditional access +- Avoid dynamic property access + +## Debugging Tips + +### 1. **Check Semicolons** +- Ensure every `with` block entry ends `with` semicolon +- Check `when` expressions in `with` blocks + +### 2. **Verify When Structure** +- Use `_ then when (condition) is` pattern +- Avoid `else` keyword +- Wrap complex conditions in parentheses + +### 3. **Validate Functions** +- Check function names against documentation +- Ensure `with rec` only has function bindings +- Verify function syntax `with` arrows and semicolons + +### 4. **Check Data Structure Syntax** +- Use explicit `key: value` pairs + +### 5. **Check Type System Usage** +- Follow type widening rules: `Int` → `Float` → `Number` +- Use `Result` type for optional values + +### 6. **Check Operator Precedence and Parentheses** +- Wrap function calls in comparisons: `(length list) > 0` +- Wrap logical expressions: `(a > 0) and (b > 0)` +- Wrap complex arithmetic: `(x + y) > z` +- See [Operator Precedence Rules](#operator-precedence-and-parenthesization-rules) for details + +## JavaScript Interop Gotchas + +When working with JavaScript interop, there are some specific gotchas to be aware of: + +### **JSValue Wrapper Behavior** + +```baba +// WRONG - Expecting direct value access +result : io.callJS "Math.abs" [-42]; +value : result.value; // This is a JSValue wrapper, not the raw number + +// CORRECT - Pass JSValue directly to other io.* functions +result : io.callJS "Math.abs" [-42]; +when result is + Ok jsValue then io.objectToTable jsValue // JSValue accepted directly + Err msg then Err msg; +``` + +### **Type Conversion Timing** + +```baba +// WRONG - Premature conversion can lose JS semantics +parsed : io.callJS "JSON.parse" [jsonString]; +table : when parsed is + Ok jsValue then io.objectToTable jsValue; // Converts immediately + +// CORRECT - Keep as JSValue until needed +parsed : io.callJS "JSON.parse" [jsonString]; +// Work with JSValue directly, convert only when needed +``` + +For comprehensive JavaScript interop documentation, see [JavaScript Interop](./09_js-interop.md). \ No newline at end of file diff --git a/js/baba-yaga/docs/08_array-programming.md b/js/baba-yaga/docs/08_array-programming.md new file mode 100644 index 0000000..1fb3fcd --- /dev/null +++ b/js/baba-yaga/docs/08_array-programming.md @@ -0,0 +1,320 @@ +# Array Programming + +Baba Yaga provides powerful array programming operations inspired by APL, K, and Q languages. These operations enable concise, expressive data transformations and mathematical computations on arrays. + +## Philosophy + +Array programming treats data as multidimensional arrays and provides operations that work on entire arrays at once, rather than element-by-element processing. This leads to: + +- **Concise Code**: Express complex operations in single function calls +- **Mathematical Clarity**: Operations mirror mathematical notation +- **Performance**: Operations are optimized for bulk data processing +- **Composability**: Operations chain together naturally + +## Indexing and Selection Operations + +### `at` - Select by Indices +Select elements from an array at specific positions: + +```baba +data : [10, 20, 30, 40, 50]; +indices : [0, 2, 4]; +selected : at indices data; // [10, 30, 50] + +// Empty indices return empty array +empty : at [] data; // [] + +// Out of bounds indices throw errors +// invalid : at [0, 10] data; // Error: Index out of bounds +``` + +### `where` - Find by Predicate +Find indices where a predicate function returns true: + +```baba +data : [10, 21, 30, 43, 50]; +evenPredicate : x -> x % 2 = 0; +evenIndices : where evenPredicate data; // [0, 2, 4] + +// Find all elements greater than 25 +largePredicate : x -> x > 25; +largeIndices : where largePredicate data; // [2, 3, 4] + +// No matches return empty array +neverTrue : x -> false; +empty : where neverTrue data; // [] +``` + +### `take` - First N Elements +Take the first n elements from an array: + +```baba +data : [1, 2, 3, 4, 5, 6]; +firstThree : take 3 data; // [1, 2, 3] +firstZero : take 0 data; // [] +all : take 10 data; // [1, 2, 3, 4, 5, 6] (all available) + +// Negative numbers throw errors +// invalid : take -1 data; // Error: take expects non-negative number +``` + +### `drop` - Remove First N Elements +Remove the first n elements from an array: + +```baba +data : [1, 2, 3, 4, 5, 6]; +lastThree : drop 3 data; // [4, 5, 6] +none : drop 10 data; // [] (dropped more than available) +all : drop 0 data; // [1, 2, 3, 4, 5, 6] (no change) + +// Negative numbers throw errors +// invalid : drop -1 data; // Error: drop expects non-negative number +``` + +## Cumulative Operations (Scan) + +### `scan` - General Cumulative Operation +Apply a binary function cumulatively across an array: + +```baba +// Custom scan with addition +addFunc : acc x -> acc + x; +numbers : [1, 2, 3, 4, 5]; +cumulative : scan addFunc 0 numbers; // [0, 1, 3, 6, 10, 15] + +// Scan with multiplication +mulFunc : acc x -> acc * x; +products : scan mulFunc 1 numbers; // [1, 1, 2, 6, 24, 120] + +// Scan with string concatenation +concatFunc : acc x -> acc .. x; +words : ["hello", " ", "world"]; +sentence : scan concatFunc "" words; // ["", "hello", "hello ", "hello world"] +``` + +### `cumsum` - Cumulative Sum +Specialized scan for addition (most common use case): + +```baba +numbers : [1, 2, 3, 4, 5]; +cumSums : cumsum numbers; // [0, 1, 3, 6, 10, 15] + +// Equivalent to: scan (acc x -> acc + x) 0 numbers +``` + +### `cumprod` - Cumulative Product +Specialized scan for multiplication: + +```baba +numbers : [1, 2, 3, 4, 5]; +cumProducts : cumprod numbers; // [1, 1, 2, 6, 24, 120] + +// Equivalent to: scan (acc x -> acc * x) 1 numbers +``` + +## Broadcasting Operations + +### `broadcast` - Scalar-Array Operations +Apply a binary operation between a scalar and each array element: + +```baba +values : [1, 2, 3, 4]; +addOp : x y -> x + y; +addTen : broadcast addOp 10 values; // [11, 12, 13, 14] + +// Subtraction +subOp : x y -> x - y; +subtract5 : broadcast subOp 5 values; // [-4, -3, -2, -1] (5 - each element) + +// Division +divOp : x y -> x / y; +reciprocals : broadcast divOp 1 values; // [1, 0.5, 0.333..., 0.25] +``` + +### `zipWith` - Element-wise Binary Operations +Apply a binary operation element-wise to two arrays: + +```baba +array1 : [1, 2, 3, 4]; +array2 : [10, 20, 30, 40]; + +// Element-wise addition +addOp : x y -> x + y; +sums : zipWith addOp array1 array2; // [11, 22, 33, 44] + +// Element-wise multiplication +mulOp : x y -> x * y; +products : zipWith mulOp array1 array2; // [10, 40, 90, 160] + +// Arrays of different lengths use minimum length +short : [1, 2]; +long : [10, 20, 30, 40]; +result : zipWith addOp short long; // [11, 22] +``` + +### `reshape` - Array Restructuring +Reshape a flat array into a multidimensional structure: + +```baba +flatData : [1, 2, 3, 4, 5, 6]; + +// Reshape into 2x3 matrix +matrix2x3 : reshape [2, 3] flatData; // 2 rows, 3 columns +// Result: [[1, 2, 3], [4, 5, 6]] + +// Reshape into 3x2 matrix +matrix3x2 : reshape [3, 2] flatData; // 3 rows, 2 columns +// Result: [[1, 2], [3, 4], [5, 6]] + +// Incompatible dimensions throw errors +// invalid : reshape [2, 4] flatData; // Error: Cannot reshape array of length 6 to [2, 4] +``` + +## Monadic Operations + +### `flatMap` - Map and Flatten +Apply a function that returns arrays, then flatten the results: + +```baba +// Duplicate each element +duplicator : x -> [x, x]; +original : [1, 2, 3]; +duplicated : flatMap duplicator original; // [1, 1, 2, 2, 3, 3] + +// Generate ranges +rangeFunc : x -> range 1 x; +ranges : flatMap rangeFunc [2, 3]; // [1, 2, 1, 2, 3] + +// Filter and transform +evenDoubles : x -> when x % 2 is 0 then [x * 2] _ then []; +numbers : [1, 2, 3, 4, 5]; +result : flatMap evenDoubles numbers; // [4, 8] +``` + +## Array Programming Patterns + +### Data Pipeline Processing +```baba +// Process sales data: filter, transform, aggregate +salesData : [100, 250, 75, 300, 150, 400, 50]; + +pipeline : data -> + with ( + // Find high-value sales (>= 200) + highValueIndices : where (x -> x >= 200) data; + highValues : at highValueIndices data; + + // Apply discount + discounted : broadcast (x y -> x * y) 0.9 highValues; + + // Calculate cumulative revenue + cumulativeRevenue : cumsum discounted; + ) -> { + original: highValues, + discounted: discounted, + cumulative: cumulativeRevenue, + total: (slice cumulativeRevenue (length cumulativeRevenue - 1) (length cumulativeRevenue)).0 + }; + +result : pipeline salesData; +``` + +### Matrix Operations +```baba +// Create and manipulate matrices +flatMatrix : [1, 2, 3, 4, 5, 6, 7, 8, 9]; +matrix3x3 : reshape [3, 3] flatMatrix; + +// Add scalar to all elements +addOp : x y -> x + y; +shifted : broadcast addOp 10 flatMatrix; +shiftedMatrix : reshape [3, 3] shifted; + +// Element-wise operations between matrices +matrix2 : [9, 8, 7, 6, 5, 4, 3, 2, 1]; +mulOp : x y -> x * y; +elementwiseProduct : zipWith mulOp flatMatrix matrix2; +productMatrix : reshape [3, 3] elementwiseProduct; +``` + +### Statistical Analysis +```baba +// Statistical operations on datasets +dataset : [23, 45, 67, 12, 89, 34, 56, 78, 90, 11]; + +analyze : data -> + with ( + sorted : sort.by data (x -> x); + n : length data; + + // Cumulative statistics + cumSums : cumsum data; + runningAverages : broadcast (x y -> x / y) (cumsum data) (range 1 (n + 1)); + + // Percentile indices + q1Index : (n + 1) / 4; + q3Index : 3 * (n + 1) / 4; + ) -> { + size: n, + total: (slice cumSums (n - 1) n).0, + runningAvgs: runningAverages, + sorted: sorted + }; + +stats : analyze dataset; +``` + +## Error Handling + +Array programming operations include comprehensive error checking: + +```baba +// Index out of bounds +data : [1, 2, 3]; +// error : at [0, 5] data; // Error: Index out of bounds + +// Invalid reshape dimensions +flatData : [1, 2, 3, 4, 5]; +// error : reshape [2, 3] flatData; // Error: Cannot reshape array of length 5 to [2, 3] + +// Type errors +// error : scan "not a function" 0 [1, 2, 3]; // Error: Scan expects a function +// error : broadcast 42 5 [1, 2, 3]; // Error: broadcast expects a function +``` + +## Performance Considerations + +- **Bulk Operations**: Array programming operations are optimized for processing entire arrays +- **Memory Efficiency**: Operations create new arrays (immutable) but reuse underlying data when possible +- **Composition**: Chain operations together for complex transformations without intermediate variables +- **Functional Style**: Pure functions with no side effects enable optimizations + +## Integration with Other Features + +Array programming operations integrate seamlessly with other Baba Yaga features: + +```baba +// With pattern matching +processArray : arr -> + when (length arr) is + 0 then [] + 1 then arr + n if (n > 10) then take 10 arr // Limit large arrays + _ then broadcast (x y -> x + y) 1 arr; // Add 1 to each element + +// With error handling using Result types +safeAt : indices data -> + when (filter (i -> i >= 0 and i < length data) indices) is + validIndices then Ok (at validIndices data) + _ then Err "Invalid indices"; + +// With higher-order functions +applyToColumns : matrix func -> + with ( + rows : length matrix; + cols : length matrix.0; + columnData : i -> map (row -> row.i) matrix; + ) -> map (i -> func (columnData i)) (range 0 cols); +``` + +Array programming in Baba Yaga provides a powerful, expressive way to work with collections of data, enabling both mathematical computations and practical data processing tasks. diff --git a/js/baba-yaga/docs/09_js-interop.md b/js/baba-yaga/docs/09_js-interop.md new file mode 100644 index 0000000..28ec7bb --- /dev/null +++ b/js/baba-yaga/docs/09_js-interop.md @@ -0,0 +1,500 @@ +# JavaScript Interop + +This document covers Baba Yaga's JavaScript interoperability features, which allow safe and controlled access to JavaScript functionality while maintaining Baba Yaga's functional programming guarantees. + +## Table of Contents + +1. [Overview](#overview) +2. [Core Functions](#core-functions) +3. [Type Conversion](#type-conversion) +4. [Security Model](#security-model) +5. [Common Patterns](#common-patterns) +6. [Error Handling](#error-handling) +7. [Configuration](#configuration) +8. [Best Practices](#best-practices) +9. [Examples](#examples) + +## Overview + +Baba Yaga's JavaScript interop system provides a safe bridge between Baba Yaga's functional, immutable world and JavaScript's imperative, mutable one. All JavaScript operations return `Result` types to maintain explicit error handling. + +### Key Principles + +- **Safety First**: All JS operations are sandboxed and return `Result` types +- **Explicit Boundaries**: Clear separation between Baba Yaga and JavaScript +- **Type Safety**: Automatic conversion between type systems +- **Error Isolation**: JavaScript errors become Baba Yaga `Err` values + +## Core Functions + +All JavaScript interop functions are available in the `io.*` namespace. + +### Function Calls + +#### `io.callJS` +Call a JavaScript function synchronously. + +```baba +io.callJS : (functionName: String, args: [Any]) -> Result + +// Examples +absResult : io.callJS "Math.abs" [-42]; +// Returns: Ok (JSValue 42) + +parseResult : io.callJS "JSON.parse" ["{\"x\": 10}"]; +// Returns: Ok (JSValue {x: 10}) + +// Note: io.callJS returns a Result whose Ok value is a JSValue wrapper +// around the raw JavaScript value. You can pass this JSValue directly to +// io.getProperty, io.setProperty, io.hasProperty, io.jsArrayToList, +// io.objectToTable, etc. without manual unwrapping. +``` + +#### `io.callJSAsync` +Call a JavaScript function asynchronously (if async operations are enabled). + +```baba +io.callJSAsync : (functionName: String, args: [Any]) -> Result + +// Example (requires enableAsyncOps: true) +fetchResult : io.callJSAsync "fetch" ["https://api.example.com/data"]; +``` + +### Property Access + +#### `io.getProperty` +Get a property from a JavaScript object. + +```baba +io.getProperty : (obj: Any, propName: String) -> Result + +// Example +obj : io.callJS "JSON.parse" ["{\"name\": \"Alice\"}"]; +nameResult : when obj is + Ok parsed then io.getProperty parsed "name" + Err msg then Err msg; +// Returns: Ok "Alice" (direct Baba Yaga string) +``` + +#### `io.setProperty` +Set a property on a JavaScript object (mutates the object). + +```baba +io.setProperty : (obj: Any, propName: String, value: Any) -> Result + +// Example +obj : io.callJS "JSON.parse" ["{}"]; +result : when obj is + Ok parsed then io.setProperty parsed "newProp" 42 + Err msg then Err msg; +``` + +#### `io.hasProperty` +Check if a property exists on a JavaScript object. + +```baba +io.hasProperty : (obj: Any, propName: String) -> Bool + +// Example +obj : io.callJS "JSON.parse" ["{\"x\": 10}"]; +hasX : when obj is + Ok parsed then io.hasProperty parsed "x" + Err _ then false; +// Returns: true +``` + +### Type Conversion + +#### `io.jsArrayToList` +Convert a JavaScript array to a Baba Yaga list. + +```baba +io.jsArrayToList : (jsArray: Any) -> Result + +// Example +jsArray : io.callJS "JSON.parse" ["[1, 2, 3]"]; +listResult : when jsArray is + Ok arr then io.jsArrayToList arr + Err msg then Err msg; +// Returns: Ok [1, 2, 3] (direct Baba Yaga list) +``` + +#### `io.listToJSArray` +Convert a Baba Yaga list to a JavaScript array. + +```baba +io.listToJSArray : (list: [Any]) -> Any + +// Example +babaList : [1, 2, 3, 4, 5]; +jsArray : io.listToJSArray babaList; +jsonResult : io.callJS "JSON.stringify" [jsArray]; +// Returns: Ok (JSValue "[1,2,3,4,5]") +``` + +#### `io.objectToTable` +Convert a JavaScript object to a Baba Yaga table. + +```baba +io.objectToTable : (obj: Any) -> Result + +// Example +jsObj : io.callJS "JSON.parse" ["{\"name\": \"Bob\", \"age\": 25}"]; +tableResult : when jsObj is + Ok obj then io.objectToTable obj + Err msg then Err msg; +// Returns: Ok {name: "Bob", age: 25} (direct Baba Yaga table) +``` + +#### `io.tableToObject` +Convert a Baba Yaga table to a JavaScript object. + +```baba +io.tableToObject : (table: Table) -> Any + +// Example +babaTable : {x: 100, y: 200}; +jsObj : io.tableToObject babaTable; +jsonResult : io.callJS "JSON.stringify" [jsObj]; +// Returns: Ok (JSValue "{\"x\":100,\"y\":200}") +``` + +### Error Management + +#### `io.getLastJSError` +Get the last JavaScript error that occurred. + +Note: depending on language syntax rules for zero-argument functions, direct invocation may not be available in all contexts. Prefer handling errors from `io.callJS` directly via the returned `Result`. + +#### `io.clearJSError` +Clear the last JavaScript error. + +Note: same invocation caveat as above applies. + +## Type Conversion + +### Automatic Conversions + +The JavaScript bridge automatically converts between Baba Yaga and JavaScript types: + +| Baba Yaga Type | JavaScript Type | Notes | +|----------------|-----------------|-------------------------------------| +| `Number` | `number` | Preserves integer/float distinction | +| `String` | `string` | Direct conversion | +| `Bool` | `boolean` | Direct conversion | +| `List` | `Array` | Recursive conversion of elements | +| `Table` | `Object` | Converts Map to plain object | +| `Result` | N/A | Handled at boundary | + + +### Manual Conversions + +For more control, use explicit conversion functions: + +```baba +// Safe JSON parsing with error handling +parseJSON : jsonString -> + when (validate.type "String" jsonString) is + false then Err "Input must be a string" + true then when (io.callJS "JSON.parse" [jsonString]) is + Ok parsed then Ok (io.objectToTable parsed) + Err msg then Err ("JSON parse error: " .. msg); + +// Usage +result : parseJSON "{\"user\": \"Alice\", \"score\": 95}"; +``` + +## Security Model + +The JavaScript interop system uses a configurable security model: + +### Sandboxed Execution + +All JavaScript code runs in a controlled sandbox with: + +- **Limited Global Access**: Only allowed globals are available +- **Function Whitelist**: Only explicitly allowed functions can be called +- **Timeout Protection**: Operations have configurable time limits +- **Memory Limits**: Configurable memory usage constraints + +### Default Allowed Functions + +By default, these JavaScript functions are available: + +```javascript +// JSON operations +'JSON.parse', 'JSON.stringify', + +// Math operations +'Math.abs', 'Math.floor', 'Math.ceil', 'Math.round', +'Math.min', 'Math.max', 'Math.random', + +// Console operations +'console.log', 'console.warn', 'console.error', + +// Time operations +'Date.now', 'performance.now' +``` + +### Configuration + +Configure the JavaScript bridge through the host configuration: + +```javascript +const host = { + jsBridgeConfig: { + allowedFunctions: new Set(['Math.abs', 'JSON.parse']), + maxExecutionTime: 5000, // 5 seconds + enableAsyncOps: false, // Disable async operations + enableFileSystem: false, // Disable file system access + enableNetwork: false // Disable network access + } +}; +``` + +## Common Patterns + +### Safe JSON Operations + +```baba +// Safe JSON parsing +safeParseJSON : jsonStr -> + when (io.callJS "JSON.parse" [jsonStr]) is + Ok obj then when (io.objectToTable obj) is + Ok table then Ok table + Err msg then Err ("Conversion error: " .. msg) + Err msg then Err ("Parse error: " .. msg); + +// Safe JSON stringification +safeStringifyJSON : table -> + jsObj : io.tableToObject table; + io.callJS "JSON.stringify" [jsObj]; +``` + +### Mathematical Operations + +```baba +// Safe mathematical operations with validation +safeMath : operation args -> + when (validate.notEmpty args) is + false then Err "No arguments provided" + true then when operation is + "abs" then io.callJS "Math.abs" [head args] + "min" then io.callJS "Math.min" args + "max" then io.callJS "Math.max" args + "round" then io.callJS "Math.round" [head args] + _ then Err ("Unknown operation: " .. operation); + +// Usage +result : safeMath "abs" [-42]; // Ok 42 +minResult : safeMath "min" [10, 5, 8]; // Ok 5 +``` + +### Working with JavaScript APIs + +```baba +// Date operations +getCurrentTimestamp : () -> + io.callJS "Date.now" []; + +formatDate : timestamp -> + when (io.callJS "Date" [timestamp]) is + Ok dateObj then io.callJS "Date.prototype.toISOString" [dateObj] + Err msg then Err msg; + +// Performance monitoring +measurePerformance : operation -> + startTime : io.callJS "performance.now" []; + result : operation; + endTime : io.callJS "performance.now" []; + + duration : when (startTime, endTime) is + (Ok start, Ok end) then Ok (end - start) + _ then Err "Could not measure performance"; + + {result: result, duration: duration}; +``` + +## Error Handling + +### JavaScript Error Types + +JavaScript errors are automatically converted to Baba Yaga `Err` values: + +```baba +// This will return an Err +result : io.callJS "JSON.parse" ["invalid json"]; +// Returns: Err "Unexpected token i in JSON at position 0" + +// Handle different error types +handleJSError : result -> + when result is + Ok value then processValue value + Err msg then when (text.contains msg "JSON") is + true then handleJSONError msg + false then handleGenericError msg; +``` + +### Error Recovery Patterns + +```baba +// Retry pattern +retryOperation : operation maxAttempts -> + attempt : 1; + + tryOperation : currentAttempt -> + when (currentAttempt > maxAttempts) is + true then Err "Max attempts exceeded" + false then when (operation) is + Ok result then Ok result + Err _ then tryOperation (currentAttempt + 1); + + tryOperation attempt; + +// Fallback pattern +withFallback : primaryOp fallbackOp -> + when primaryOp is + Ok result then Ok result + Err _ then fallbackOp; +``` + +## Best Practices + +### 1. Always Use Result Types + +Never assume JavaScript operations will succeed: + +```baba +// Good +result : when (io.callJS "Math.abs" [value]) is + Ok abs then processValue abs + Err msg then handleError msg; + +// Bad - assumes success +abs : io.callJS "Math.abs" [value]; // This returns Result, not number +``` + +### 2. Validate Inputs + +Always validate data before sending to JavaScript: + +```baba +// Good +safeCall : value -> + when (validate.type "Number" value) is + false then Err "Value must be a number" + true then io.callJS "Math.abs" [value]; + +// Bad - no validation +unsafeCall : value -> + io.callJS "Math.abs" [value]; +``` + +### 3. Handle Type Conversions Explicitly + +Be explicit about type conversions: + +```baba +// Good +processJSData : jsData -> + when (io.objectToTable jsData) is + Ok table then processTable table + Err msg then Err ("Conversion failed: " .. msg); + +// Bad - assumes conversion works +processJSData : jsData -> + table : io.objectToTable jsData; + processTable table; +``` + +### 4. Use Composition for Complex Operations + +Break complex JavaScript interactions into smaller, composable functions: + +```baba +// Composed operations +parseAndValidate : jsonStr schema -> + parsed : safeParseJSON jsonStr; + when parsed is + Ok data then validateAgainstSchema data schema + Err msg then Err msg; + +transformAndStringify : data transformer -> + transformed : transformer data; + safeStringifyJSON transformed; +``` + +## Examples + +### Complete JSON Processing Pipeline + +```baba +// Complete JSON processing with error handling +processJSONData : jsonString -> + // Parse JSON + parseResult : io.callJS "JSON.parse" [jsonString]; + + when parseResult is + Err msg then Err ("Parse failed: " .. msg) + Ok jsObj then + // Convert to Baba Yaga table + when (io.objectToTable jsObj) is + Err msg then Err ("Conversion failed: " .. msg) + Ok table then + // Process the data + processedTable : processData table; + + // Convert back to JS object + jsResult : io.tableToObject processedTable; + + // Stringify result + io.callJS "JSON.stringify" [jsResult]; + +// Helper function +processData : table -> + // Add timestamp + withTimestamp : table .. {timestamp: getCurrentTimestamp}; + + // Validate required fields + when (hasRequiredFields withTimestamp) is + false then table .. {error: "Missing required fields"} + true then withTimestamp; + +// Usage +input : "{\"name\": \"Alice\", \"score\": 95}"; +result : processJSONData input; +// Returns: Ok (JSValue "{\"name\":\"Alice\",\"score\":95,\"timestamp\":1640995200000}") +``` + +### Working with JavaScript Arrays + +```baba +// Process JavaScript arrays +processJSArray : jsArrayString -> + // Parse array + arrayResult : io.callJS "JSON.parse" [jsArrayString]; + + when arrayResult is + Err msg then Err msg + Ok jsArray then + // Convert to Baba Yaga list + when (io.jsArrayToList jsArray) is + Err msg then Err msg + Ok babaList then + // Process with Baba Yaga functions + processed : map (x -> x * 2) babaList; + filtered : filter (x -> x > 10) processed; + + // Convert back to JS array + jsResult : io.listToJSArray filtered; + + // Return as JSON + io.callJS "JSON.stringify" [jsResult]; + +// Usage +input : "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"; +result : processJSArray input; +// Returns: Ok (JSValue "[4,6,8,10,12,14,16,18,20]") +``` + +This JavaScript interop system provides a safe, controlled way to leverage JavaScript's ecosystem while maintaining Baba Yaga's functional programming principles and explicit error handling. diff --git a/js/baba-yaga/docs/README.md b/js/baba-yaga/docs/README.md new file mode 100644 index 0000000..30f8700 --- /dev/null +++ b/js/baba-yaga/docs/README.md @@ -0,0 +1,82 @@ +# Baba Yaga Documentation + +This directory contains comprehensive documentation for the Baba Yaga functional programming language. + +## Documentation Structure + +### Core Documentation + +- **[00_crash-course.md](./00_crash-course.md)** - Complete language overview for quick reference (ideal for LLMs and quick onboarding) +- **[01_functional.md](./01_functional.md)** - Functional programming concepts, higher-order functions, and function combinators +- **[02_data-structures.md](./02_data-structures.md)** - Lists, tables, and array programming operations +- **[03_pattern-matching.md](./03_pattern-matching.md)** - Pattern matching syntax, guards, and advanced patterns +- **[04_types.md](./04_types.md)** - Optional type system, runtime validation, and type inference +- **[05_recursion-and-composition.md](./05_recursion-and-composition.md)** - Recursive functions, mutual recursion, and function composition +- **[06_error-handling.md](./06_error-handling.md)** - Result types, assertions, validation, and error handling patterns +- **[07_gotchyas.md](./07_gotchyas.md)** - Common syntax pitfalls and strict requirements +- **[08_array-programming.md](./08_array-programming.md)** - Comprehensive guide to array programming operations +- **[09_js-interop.md](./09_js-interop.md)** - JavaScript interoperability and safe JS integration + +## Topic Coverage + +### Language Fundamentals +- **Syntax**: Variables, functions, data types, operators (00, 07) +- **Data Types**: Numbers, strings, booleans, lists, tables (00, 02) +- **Functions**: Anonymous functions, currying, partial application (00, 01) +- **Control Flow**: `when` expressions, pattern matching (00, 03) + +### Advanced Features +- **Pattern Matching**: Literals, wildcards, types, guards, multiple discriminants (03) +- **Type System**: Optional types, runtime validation, function signatures (04) +- **Error Handling**: Result types, assertions, validation patterns (06) +- **Recursion**: Simple, tail, and mutual recursion (05) + +### Functional Programming +- **Higher-Order Functions**: `map`, `filter`, `reduce` (01) +- **Function Combinators**: `flip`, `apply`, `pipe`, `compose` (01) +- **Array Programming**: Indexing, scanning, broadcasting, reshaping (02, 08) +- **Monadic Operations**: `flatMap` and data transformation (01, 02, 08) + +### Standard Library +- **Array Operations**: `scan`, `cumsum`, `at`, `where`, `take`, `drop`, `broadcast`, `zipWith`, `reshape` (02, 08) +- **Math Functions**: Arithmetic, trigonometry, random numbers (00) +- **String Processing**: Manipulation, formatting, validation (00) +- **Utilities**: Sorting, grouping, debugging, validation (00, 06) +- **JavaScript Interop**: Safe JS function calls, property access, type conversion (09) + +## Documentation Principles + +1. **Non-Duplicative**: Each concept is documented in one primary location with cross-references +2. **Comprehensive**: All language features and standard library functions are covered +3. **Hierarchical**: Start with crash course, then dive into specific topics +4. **Practical**: Includes working examples and common patterns +5. **Error-Aware**: Documents error cases and safe usage patterns + +## Reading Path + +### For New Users +1. [Crash Course](./00_crash-course.md) - Complete overview +2. [Functional Programming](./01_functional.md) - Core concepts +3. [Data Structures](./02_data-structures.md) - Working with data +4. [Pattern Matching](./03_pattern-matching.md) - Control flow + +### For Specific Topics +- **Array Processing**: [Data Structures](./02_data-structures.md) → [Array Programming](./08_array-programming.md) +- **Advanced Functions**: [Functional Programming](./01_functional.md) → [Recursion & Composition](./05_recursion-and-composition.md) +- **Robust Code**: [Error Handling](./06_error-handling.md) → [Types](./04_types.md) +- **Troubleshooting**: [Gotchas](./07_gotchyas.md) +- **JavaScript Integration**: [JavaScript Interop](./09_js-interop.md) + +### For LLMs and Quick Reference +- [Crash Course](./00_crash-course.md) provides complete context in a single document + +## Cross-References + +Documentation includes appropriate cross-references to avoid duplication: +- Pattern guards are detailed in [Pattern Matching](./03_pattern-matching.md), referenced in [Functional Programming](./01_functional.md) +- Function combinators are detailed in [Functional Programming](./01_functional.md), referenced in [Recursion & Composition](./05_recursion-and-composition.md) +- Array programming is covered in both [Data Structures](./02_data-structures.md) (overview) and [Array Programming](./08_array-programming.md) (comprehensive) +- Error handling patterns for array operations are in [Error Handling](./06_error-handling.md) +- JavaScript interop strategies and gotchas are in [JavaScript Interop](./09_js-interop.md), with basic gotchas in [Gotchas](./07_gotchyas.md) + +This structure ensures comprehensive coverage while maintaining clarity and avoiding redundancy. diff --git a/js/baba-yaga/docs/ref.txt b/js/baba-yaga/docs/ref.txt new file mode 100644 index 0000000..88320fe --- /dev/null +++ b/js/baba-yaga/docs/ref.txt @@ -0,0 +1,213 @@ +BABA YAGA LANGUAGE REFERENCE +============================ + +SYNTAX +------ +var : value; // assignment +var Type; var : value; // typed assignment +f : x -> body; // function +f : x y -> body; // multi-param function +f : (x: Type) -> Type -> body; // typed function +f : x -> y -> body; // curried function +f : x -> with (locals) -> body; // with locals +f : x -> with rec (fns) -> body;// with mutual recursion + +LITERALS +-------- +42 // Int +3.14 // Float +"text" // String +true false // Bool +[1,2,3] // List +{a:1, b:2} // Table +PI INFINITY // constants + +OPERATORS (precedence high→low) +------------------------------- +f x, obj.prop // call, access +- ! // unary minus, not +* / % // multiply, divide, modulo ++ - // add, subtract += != < <= > >= // comparison +and or // logical +.. // string concat + +CONTROL FLOW +------------ +when x is // pattern match + 0 then "zero" + Int then "number" + _ then "other"; + +when x y is // multi-discriminant + 0 0 then "origin" + _ _ then "other"; + +x if (condition) then result // pattern guard +_ then "fallback"; + +TYPES +----- +Int Float Number String Bool List Table Result Function + +Int ⊂ Float ⊂ Number // type hierarchy +Ok value | Err message // Result variants + +ARRAY OPERATIONS +---------------- +// Core HOFs +map f xs // [f x | x <- xs] +filter p xs // [x | x <- xs, p x] +reduce f z xs // f(...f(f(z,x1),x2)...,xn) + +// Array programming +scan f z xs // cumulative reduce +cumsum xs // cumulative sum +cumprod xs // cumulative product +at indices xs // xs[indices] +where p xs // indices where p x is true +take n xs // first n elements +drop n xs // drop first n elements +broadcast f scalar xs // f scalar to each x +zipWith f xs ys // [f x y | (x,y) <- zip xs ys] +reshape dims xs // reshape flat array to matrix +flatMap f xs // concat (map f xs) + +// List manipulation +append xs x // xs ++ [x] +prepend x xs // [x] ++ xs +concat xs ys // xs ++ ys +update xs i x // xs with xs[i] = x +removeAt xs i // xs without xs[i] +slice xs start end // xs[start:end] +length xs // |xs| + +// Utilities +chunk xs n // split xs into chunks of size n +range start end // [start..end] +repeat n x // [x,x,...] (n times) +sort.by xs f // sort xs by key function f +group.by xs f // group xs by key function f + +TABLE OPERATIONS +---------------- +set tbl k v // tbl with tbl[k] = v +remove tbl k // tbl without tbl[k] +merge tbl1 tbl2 // tbl1 ∪ tbl2 +keys tbl // [k | k <- tbl] +values tbl // [tbl[k] | k <- tbl] +shape x // metadata: kind, rank, shape, size + +STRING OPERATIONS +----------------- +str.concat s1 s2 ... // s1 + s2 + ... +str.split s delim // split s by delim +str.join xs delim // join xs with delim +str.length s // |s| +str.substring s start end // s[start:end] +str.replace s old new // replace old with new in s +str.trim s // strip whitespace +str.upper s // uppercase +str.lower s // lowercase +text.lines s // split by newlines +text.words s // split by whitespace + +MATH OPERATIONS +--------------- +// Arithmetic +math.abs x // |x| +math.sign x // -1, 0, or 1 +math.min x y, math.max x y // min/max +math.clamp x lo hi // clamp x to [lo,hi] + +// Rounding +math.floor x, math.ceil x // ⌊x⌋, ⌈x⌉ +math.round x, math.trunc x // round, truncate + +// Powers & logs +math.pow x y // x^y +math.sqrt x // √x +math.exp x, math.log x // e^x, ln(x) + +// Trigonometry +math.sin x, math.cos x, math.tan x +math.asin x, math.acos x, math.atan x, math.atan2 y x +math.deg x, math.rad x // degrees ↔ radians + +// Random +math.random // [0,1) +math.randomInt lo hi // [lo,hi] + +FUNCTION COMBINATORS +-------------------- +flip f // λx y. f y x +apply f x // f x +pipe x f // f x (reverse apply) +compose f g // λx. f (g x) (binary compose) + +VALIDATION & DEBUG +------------------ +// Validation +validate.notEmpty x // x is not empty +validate.range lo hi x // lo ≤ x ≤ hi +validate.type "Type" x // x has type Type +validate.email x // x is valid email + +// Debugging +debug.print [name] value // print with optional name +debug.inspect x // detailed inspection +assert condition message // throw if condition false + +I/O +--- +io.out value // print value +io.in // read stdin + +JAVASCRIPT INTEROP +------------------ +io.callJS fnName args // call JS function synchronously +io.callJSAsync fnName args // call JS function asynchronously +io.getProperty obj propName // get JS object property +io.setProperty obj propName val // set JS object property +io.hasProperty obj propName // check if JS property exists +io.jsArrayToList jsArray // convert JS array to Baba Yaga list +io.listToJSArray list // convert Baba Yaga list to JS array +io.objectToTable jsObj // convert JS object to Baba Yaga table +io.tableToObject table // convert Baba Yaga table to JS object +io.getLastJSError // get last JS error (if available) +io.clearJSError // clear last JS error (if available) + +EXAMPLES +-------- +// Fibonacci +fib : n -> when n is 0 then 0 1 then 1 _ then (fib (n-1)) + (fib (n-2)); + +// Array processing pipeline +process : xs -> + with ( + filtered : filter (x -> (x % 2) = 0) xs; + doubled : map (x -> x * 2) filtered; + summed : reduce (acc x -> acc + x) 0 doubled; + ) -> summed; + +// Result handling +safeDivide : x y -> when y is 0 then Err "div by zero" _ then Ok (x / y); +use : r -> when r is Ok v then v Err _ then 0; + +// Pattern matching with guards +classify : x -> when x is + n if ((n > 0) and (n < 10)) then "small positive" + n if (n >= 10) then "large positive" + n if (n < 0) then "negative" + _ then "zero"; + +// Mutual recursion +evenOdd : n -> with rec ( + even : x -> when x is 0 then true _ then odd (x - 1); + odd : x -> when x is 0 then false _ then even (x - 1); +) -> {even: even n, odd: odd n}; + +// Array programming +matrix : reshape [2,3] [1,2,3,4,5,6]; // [[1,2,3],[4,5,6]] +indices : where (x -> x > 3) [1,2,3,4,5]; // [3,4] +selected : at indices [10,20,30,40,50]; // [40,50] \ No newline at end of file |