kumi 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +51 -6
  3. data/README.md +173 -51
  4. data/{documents → docs}/AST.md +29 -29
  5. data/{documents → docs}/SYNTAX.md +93 -1
  6. data/docs/features/README.md +45 -0
  7. data/docs/features/analysis-cascade-mutual-exclusion.md +89 -0
  8. data/docs/features/analysis-type-inference.md +42 -0
  9. data/docs/features/analysis-unsat-detection.md +71 -0
  10. data/docs/features/array-broadcasting.md +170 -0
  11. data/docs/features/input-declaration-system.md +42 -0
  12. data/docs/features/performance.md +16 -0
  13. data/examples/federal_tax_calculator_2024.rb +11 -6
  14. data/lib/kumi/analyzer/constant_evaluator.rb +1 -1
  15. data/lib/kumi/analyzer/passes/broadcast_detector.rb +251 -0
  16. data/lib/kumi/analyzer/passes/{definition_validator.rb → declaration_validator.rb} +4 -4
  17. data/lib/kumi/analyzer/passes/dependency_resolver.rb +72 -32
  18. data/lib/kumi/analyzer/passes/input_collector.rb +90 -29
  19. data/lib/kumi/analyzer/passes/pass_base.rb +1 -1
  20. data/lib/kumi/analyzer/passes/semantic_constraint_validator.rb +9 -9
  21. data/lib/kumi/analyzer/passes/toposorter.rb +42 -6
  22. data/lib/kumi/analyzer/passes/type_checker.rb +32 -10
  23. data/lib/kumi/analyzer/passes/type_inferencer.rb +126 -17
  24. data/lib/kumi/analyzer/passes/unsat_detector.rb +133 -53
  25. data/lib/kumi/analyzer/passes/visitor_pass.rb +2 -2
  26. data/lib/kumi/analyzer.rb +11 -12
  27. data/lib/kumi/compiler.rb +194 -16
  28. data/lib/kumi/constraint_relationship_solver.rb +6 -6
  29. data/lib/kumi/domain/validator.rb +0 -4
  30. data/lib/kumi/explain.rb +20 -20
  31. data/lib/kumi/export/node_registry.rb +26 -12
  32. data/lib/kumi/export/node_serializers.rb +1 -1
  33. data/lib/kumi/function_registry/collection_functions.rb +14 -9
  34. data/lib/kumi/function_registry/function_builder.rb +4 -3
  35. data/lib/kumi/function_registry.rb +8 -2
  36. data/lib/kumi/input/type_matcher.rb +3 -0
  37. data/lib/kumi/input/validator.rb +0 -3
  38. data/lib/kumi/parser/declaration_reference_proxy.rb +36 -0
  39. data/lib/kumi/parser/dsl_cascade_builder.rb +3 -3
  40. data/lib/kumi/parser/expression_converter.rb +6 -6
  41. data/lib/kumi/parser/input_builder.rb +40 -9
  42. data/lib/kumi/parser/input_field_proxy.rb +46 -0
  43. data/lib/kumi/parser/input_proxy.rb +3 -3
  44. data/lib/kumi/parser/nested_input.rb +15 -0
  45. data/lib/kumi/parser/schema_builder.rb +10 -9
  46. data/lib/kumi/parser/sugar.rb +61 -9
  47. data/lib/kumi/syntax/array_expression.rb +15 -0
  48. data/lib/kumi/syntax/call_expression.rb +11 -0
  49. data/lib/kumi/syntax/cascade_expression.rb +11 -0
  50. data/lib/kumi/syntax/case_expression.rb +11 -0
  51. data/lib/kumi/syntax/declaration_reference.rb +11 -0
  52. data/lib/kumi/syntax/hash_expression.rb +11 -0
  53. data/lib/kumi/syntax/input_declaration.rb +12 -0
  54. data/lib/kumi/syntax/input_element_reference.rb +12 -0
  55. data/lib/kumi/syntax/input_reference.rb +12 -0
  56. data/lib/kumi/syntax/literal.rb +11 -0
  57. data/lib/kumi/syntax/trait_declaration.rb +11 -0
  58. data/lib/kumi/syntax/value_declaration.rb +11 -0
  59. data/lib/kumi/vectorization_metadata.rb +108 -0
  60. data/lib/kumi/version.rb +1 -1
  61. metadata +31 -14
  62. data/lib/kumi/domain.rb +0 -8
  63. data/lib/kumi/input.rb +0 -8
  64. data/lib/kumi/syntax/declarations.rb +0 -26
  65. data/lib/kumi/syntax/expressions.rb +0 -34
  66. data/lib/kumi/syntax/terminal_expressions.rb +0 -30
  67. data/lib/kumi/syntax.rb +0 -9
  68. /data/{documents → docs}/DSL.md +0 -0
  69. /data/{documents → docs}/FUNCTIONS.md +0 -0
@@ -10,6 +10,7 @@ This document provides a comprehensive comparison of Kumi's DSL syntax showing b
10
10
  - [Trait Declarations](#trait-declarations)
11
11
  - [Expressions](#expressions)
12
12
  - [Functions](#functions)
13
+ - [Array Broadcasting](#array-broadcasting)
13
14
  - [Cascade Logic](#cascade-logic)
14
15
  - [References](#references)
15
16
 
@@ -187,7 +188,7 @@ value :sorted_scores, fn(:sort, input.score_array)
187
188
 
188
189
  ### Built-in Functions Available
189
190
 
190
- See [FUNCTIONS.md](documents/FUNCTIONS.md)
191
+ See [FUNCTIONS.md](FUNCTIONS.md)
191
192
 
192
193
  | Category | Sugar | Sugar-Free |
193
194
  |----------|-------|------------|
@@ -210,6 +211,97 @@ value :clamped_score, fn(:clamp, input.raw_score, 0, 1600)
210
211
  value :formatted_name, fn(:add, fn(:add, input.first_name, " "), input.last_name)
211
212
  ```
212
213
 
214
+ ## Array Broadcasting
215
+
216
+ Array broadcasting enables element-wise operations on array fields with automatic vectorization.
217
+
218
+ ### Array Input Declarations
219
+
220
+ ```ruby
221
+ input do
222
+ array :line_items do
223
+ float :price
224
+ integer :quantity
225
+ string :category
226
+ end
227
+
228
+ array :orders do
229
+ array :items do
230
+ hash :product do
231
+ string :name
232
+ float :base_price
233
+ end
234
+ integer :quantity
235
+ end
236
+ end
237
+ end
238
+ ```
239
+
240
+ ### Element-wise Operations
241
+
242
+ ```ruby
243
+ # With Sugar - Automatic Broadcasting
244
+ value :subtotals, input.line_items.price * input.line_items.quantity
245
+ trait :is_taxable, (input.line_items.category != "digital")
246
+ value :discounted_prices, input.line_items.price * 0.9
247
+
248
+ # Sugar-Free - Explicit Broadcasting
249
+ value :subtotals, fn(:multiply, input.line_items.price, input.line_items.quantity)
250
+ trait :is_taxable, fn(:!=, input.line_items.category, "digital")
251
+ value :discounted_prices, fn(:multiply, input.line_items.price, 0.9)
252
+ ```
253
+
254
+ ### Aggregation Operations
255
+
256
+ ```ruby
257
+ # With Sugar - Automatic Aggregation Detection
258
+ value :total_subtotal, fn(:sum, subtotals)
259
+ value :avg_price, fn(:avg, input.line_items.price)
260
+ value :max_quantity, fn(:max, input.line_items.quantity)
261
+ value :item_count, fn(:size, input.line_items)
262
+
263
+ # Sugar-Free - Same Syntax
264
+ value :total_subtotal, fn(:sum, subtotals)
265
+ value :avg_price, fn(:avg, input.line_items.price)
266
+ value :max_quantity, fn(:max, input.line_items.quantity)
267
+ value :item_count, fn(:size, input.line_items)
268
+ ```
269
+
270
+ ### Nested Array Access
271
+
272
+ ```ruby
273
+ # With Sugar - Deep Field Access
274
+ value :all_product_names, input.orders.items.product.name
275
+ value :total_values, input.orders.items.product.base_price * input.orders.items.quantity
276
+
277
+ # Sugar-Free - Same Deep Access
278
+ value :all_product_names, input.orders.items.product.name
279
+ value :total_values, fn(:multiply, input.orders.items.product.base_price, input.orders.items.quantity)
280
+ ```
281
+
282
+ ### Mixed Operations
283
+
284
+ ```ruby
285
+ # With Sugar - Element-wise then Aggregation
286
+ value :line_totals, input.items.price * input.items.quantity
287
+ value :order_total, fn(:sum, line_totals)
288
+ value :avg_line_total, fn(:avg, line_totals)
289
+ trait :has_expensive, fn(:any?, expensive_items)
290
+
291
+ # Sugar-Free - Same Pattern
292
+ value :line_totals, fn(:multiply, input.items.price, input.items.quantity)
293
+ value :order_total, fn(:sum, line_totals)
294
+ value :avg_line_total, fn(:avg, line_totals)
295
+ trait :has_expensive, fn(:any?, expensive_items)
296
+ ```
297
+
298
+ ### Broadcasting Type Inference
299
+
300
+ The type system automatically infers appropriate types:
301
+ - `input.items.price` (float array) → inferred as `:float` per element
302
+ - `input.items.price * input.items.quantity` → element-wise `:float` result
303
+ - `fn(:sum, input.items.price)` → scalar `:float` result
304
+
213
305
  ## Cascade Logic
214
306
 
215
307
  Cascades are similar to Ruby when case, where each case is one of more trait reference and finally the value if that branch is true.
@@ -0,0 +1,45 @@
1
+ # Features
2
+
3
+ ## Core Features
4
+
5
+ ### [Unsatisfiability Detection](analysis-unsat-detection.md)
6
+ Analyzes rule combinations to detect logical impossibilities across dependency chains.
7
+
8
+ - Detects impossible combinations at compile-time
9
+ - Validates domain constraints
10
+ - Reports multiple errors
11
+
12
+ ### [Cascade Mutual Exclusion](analysis-cascade-mutual-exclusion.md)
13
+ Enables safe mutual recursion when cascade conditions are mutually exclusive.
14
+
15
+ - Allows mathematically sound recursive patterns
16
+ - Detects mutually exclusive conditions
17
+ - Prevents unsafe cycles while enabling safe ones
18
+
19
+ ### [Type Inference](analysis-type-inference.md)
20
+ Determines types from expressions and propagates them through dependencies.
21
+
22
+ - Infers types from literals and function calls
23
+ - Propagates types through dependency graph
24
+ - Validates function arguments
25
+
26
+ ### [Input Declarations](input-declaration-system.md)
27
+ Defines expected inputs with types and constraints.
28
+
29
+ - Type-specific declaration methods
30
+ - Domain validation at runtime
31
+ - Separates input metadata from business logic
32
+
33
+ ### [Performance](performance.md)
34
+ TODO: Add benchmark data
35
+ Processes large schemas with optimized algorithms.
36
+
37
+ - Result caching
38
+ - Selective evaluation
39
+
40
+ ## Integration
41
+
42
+ - Type inference uses input declarations
43
+ - Unsatisfiability detection uses type information for validation
44
+ - Cascade mutual exclusion integrates with dependency analysis and cycle detection
45
+ - Performance optimizations apply to all analysis passes
@@ -0,0 +1,89 @@
1
+ # Cascade Mutual Exclusion Detection
2
+
3
+ Analyzes cascade expressions to allow safe recursive patterns when conditions are mutually exclusive.
4
+
5
+ ## Overview
6
+
7
+ The cascade mutual exclusion detector identifies when all conditions in a cascade expression cannot be true simultaneously, enabling safe mutual recursion patterns that would otherwise be rejected as cycles.
8
+
9
+ ## Core Mechanism
10
+
11
+ The system performs three-stage analysis:
12
+
13
+ 1. **Conditional Dependency Tracking** - DependencyResolver marks base case dependencies as conditional
14
+ 2. **Mutual Exclusion Analysis** - UnsatDetector determines if cascade conditions are mutually exclusive
15
+ 3. **Safe Cycle Detection** - Toposorter allows cycles where all edges are conditional and conditions are mutually exclusive
16
+
17
+ ## Example: Processing Workflow
18
+
19
+ ```ruby
20
+ schema do
21
+ input do
22
+ string :operation # "forward", "reverse", "unknown"
23
+ integer :value
24
+ end
25
+
26
+ trait :is_forward, input.operation == "forward"
27
+ trait :is_reverse, input.operation == "reverse"
28
+
29
+ # Safe mutual recursion - conditions are mutually exclusive
30
+ value :forward_processor do
31
+ on is_forward, input.value * 2 # Direct calculation
32
+ on is_reverse, reverse_processor + 10 # Delegates to reverse (safe)
33
+ base "invalid operation" # Fallback for unknown operations
34
+ end
35
+
36
+ value :reverse_processor do
37
+ on is_forward, forward_processor - 5 # Delegates to forward (safe)
38
+ on is_reverse, input.value / 2 # Direct calculation
39
+ base "invalid operation" # Fallback for unknown operations
40
+ end
41
+ end
42
+ ```
43
+
44
+ ## Safety Guarantees
45
+
46
+ **Allowed**: Cycles where conditions are mutually exclusive
47
+ - `is_forward` and `is_reverse` cannot both be true (operation has single value)
48
+ - Each recursion executes exactly one step before hitting direct calculation
49
+ - Bounded recursion with guaranteed termination
50
+
51
+ **Rejected**: Cycles with overlapping conditions
52
+ ```ruby
53
+ # This would be rejected - conditions can overlap
54
+ value :unsafe_cycle do
55
+ on input.n > 0, "positive"
56
+ on input.n > 5, "large" # Both can be true!
57
+ base fn(:not, unsafe_cycle)
58
+ end
59
+ ```
60
+
61
+ ## Implementation Details
62
+
63
+ ### Conditional Dependencies
64
+ Base case dependencies are marked as conditional because they only execute when no explicit conditions match.
65
+
66
+ ### Mutual Exclusion Analysis
67
+ Conditions are analyzed for mutual exclusion:
68
+ - Same field equality comparisons: `field == value1` vs `field == value2`
69
+ - Domain constraints ensuring impossibility
70
+ - All condition pairs must be mutually exclusive
71
+
72
+ ### Metadata Generation
73
+ Analysis results stored in `cascade_metadata` state:
74
+ ```ruby
75
+ {
76
+ condition_traits: [:is_forward, :is_reverse],
77
+ condition_count: 2,
78
+ all_mutually_exclusive: true,
79
+ exclusive_pairs: 1,
80
+ total_pairs: 1
81
+ }
82
+ ```
83
+
84
+ ## Use Cases
85
+
86
+ - Processing workflows with bidirectional logic
87
+ - State machine fallback patterns
88
+ - Recursive decision trees with termination conditions
89
+ - Complex business rules with safe delegation patterns
@@ -0,0 +1,42 @@
1
+ # Type Inference
2
+
3
+ Infers types from expressions and propagates them through dependency chains for compile-time type checking.
4
+
5
+ ## Type Inference Rules
6
+
7
+ **Literals:**
8
+ - `42` → `:integer`
9
+ - `3.14` → `:float`
10
+ - `"hello"` → `:string`
11
+ - `true` → `:boolean`
12
+ - `[1, 2, 3]` → `{ array: :integer }`
13
+
14
+ **Operations:**
15
+ - Integer arithmetic → `:integer`
16
+ - Mixed numeric operations → `:numeric`
17
+ - Comparisons → `:boolean`
18
+
19
+ **Functions:**
20
+ - Return types defined in function registry
21
+ - Arguments validated against parameter types
22
+
23
+ ## Error Detection
24
+
25
+ **Type mismatches:**
26
+ ```
27
+ TypeError: argument 2 of addition expects numeric, got input field `age` of declared type integer,
28
+ but argument 1 is input field `customer_name` of declared type string
29
+ ```
30
+
31
+ **Function validation:**
32
+ - Arity validation (correct number of arguments)
33
+ - Type compatibility validation
34
+ - Unknown function detection
35
+
36
+ ## Inference Process
37
+
38
+ - Processes declarations in topological order to resolve dependencies
39
+ - Literal types determined from values
40
+ - Function return types from function registry
41
+ - Array types unified from element types
42
+ - Cascade types inferred from result expressions
@@ -0,0 +1,71 @@
1
+ # Unsatisfiability Detection
2
+
3
+ Analyzes logical relationships across dependency chains to detect impossible rule combinations at compile time.
4
+
5
+ ## Example: Credit Card Approval System
6
+
7
+ This schema contains impossible rule combinations:
8
+
9
+ ```ruby
10
+ module CreditCardApproval
11
+ extend Kumi::Schema
12
+
13
+ schema do
14
+ input do
15
+ integer :annual_income
16
+ integer :monthly_debt
17
+ integer :credit_score, domain: 300..850
18
+ string :employment_type, domain: %w[full_time part_time contract self_employed]
19
+ end
20
+
21
+ # Financial calculations
22
+ value :monthly_income, input.annual_income / 12
23
+ value :available_monthly, monthly_income - input.monthly_debt
24
+ value :credit_limit_base, input.annual_income * 0.3
25
+ value :score_multiplier, (input.credit_score - 600) * 0.01
26
+ value :final_credit_limit, credit_limit_base * score_multiplier
27
+ value :employment_stability_factor,
28
+ input.employment_type == "full_time" ? 1.0 :
29
+ input.employment_type == "part_time" ? 0.7 :
30
+ input.employment_type == "contract" ? 0.5 : 0.3
31
+
32
+ # Business rules
33
+ trait :stable_income, (input.annual_income >= 60_000)
34
+ trait :good_credit, (input.credit_score >= 700)
35
+ trait :high_available_income, (available_monthly >= 4_000)
36
+ trait :premium_limit_qualified, (final_credit_limit >= 50_000)
37
+ trait :stable_employment, (input.employment_type == "full_time")
38
+ trait :high_stability_factor, (employment_stability_factor >= 0.8)
39
+ trait :excellent_credit, (input.credit_score == 900)
40
+
41
+ # Approval tiers - which combinations are impossible?
42
+ value :approval_tier do
43
+ on stable_income,good_credit,premium_limit_qualified, "platinum_tier"
44
+ on stable_employment,high_stability_factor, "executive_tier"
45
+ on excellent_credit,good_credit, "perfect_score_tier"
46
+ on stable_income,good_credit, "standard_tier"
47
+ base "manual_review"
48
+ end
49
+ end
50
+ end
51
+ ```
52
+
53
+ **Detected errors:**
54
+
55
+ ```
56
+ SemanticError:
57
+ conjunction `excellent_credit AND good_credit` is impossible
58
+ conjunction `excellent_credit` is impossible
59
+ ```
60
+
61
+ **Root cause:**
62
+ - `excellent_credit` requires `credit_score == 900`
63
+ - Input domain constrains `credit_score` to `300..850`
64
+ - Any cascade condition using `excellent_credit` becomes impossible
65
+
66
+ ## Detection Mechanisms
67
+
68
+ - Domain constraint violations: `field == value` where value is outside declared domain
69
+ - Cascade condition analysis: each `on` condition checked independently
70
+ - OR expression handling: impossible only if both sides are impossible
71
+ - Cross-variable mathematical constraint analysis
@@ -0,0 +1,170 @@
1
+ # Array Broadcasting
2
+
3
+ Automatic vectorization of operations over array fields with element-wise computation and aggregation.
4
+
5
+ ## Overview
6
+
7
+ The array broadcasting system enables natural field access syntax on array inputs (`input.items.price`) that automatically applies operations element-wise across the array, with intelligent detection of map vs reduce operations.
8
+
9
+ ## Core Mechanism
10
+
11
+ The system uses a three-stage pipeline:
12
+
13
+ 1. **Parser** - Creates InputElementReference AST nodes for nested field access
14
+ 2. **BroadcastDetector** - Identifies which operations should be vectorized vs scalar
15
+ 3. **Compiler** - Generates appropriate map/reduce functions based on usage context
16
+
17
+ ## Basic Broadcasting
18
+
19
+ ```ruby
20
+ schema do
21
+ input do
22
+ array :line_items do
23
+ float :price
24
+ integer :quantity
25
+ string :category
26
+ end
27
+ scalar :tax_rate, type: :float
28
+ end
29
+
30
+ # Element-wise computation - broadcasts over each item
31
+ value :subtotals, input.line_items.price * input.line_items.quantity
32
+
33
+ # Element-wise traits - applied to each item
34
+ trait :is_taxable, (input.line_items.category != "digital")
35
+
36
+ # Conditional logic - element-wise evaluation
37
+ value :taxes, fn(:if, is_taxable, subtotals * input.tax_rate, 0.0)
38
+ end
39
+ ```
40
+
41
+ ## Aggregation Operations
42
+
43
+ Operations that consume arrays to produce scalars are automatically detected:
44
+
45
+ ```ruby
46
+ schema do
47
+ # These aggregate the vectorized results
48
+ value :total_subtotal, fn(:sum, subtotals)
49
+ value :total_tax, fn(:sum, taxes)
50
+ value :grand_total, total_subtotal + total_tax
51
+
52
+ # Statistics over arrays
53
+ value :avg_price, fn(:avg, input.line_items.price)
54
+ value :max_quantity, fn(:max, input.line_items.quantity)
55
+ end
56
+ ```
57
+
58
+ ## Field Access Nesting
59
+
60
+ Supports arbitrary depth field access with path building:
61
+
62
+ ```ruby
63
+ schema do
64
+ input do
65
+ array :orders do
66
+ array :items do
67
+ hash :product do
68
+ string :name
69
+ float :base_price
70
+ end
71
+ integer :quantity
72
+ end
73
+ end
74
+ end
75
+
76
+ # Deep field access - automatically broadcasts over nested arrays
77
+ value :all_product_names, input.orders.items.product.name
78
+ value :total_values, input.orders.items.product.base_price * input.orders.items.quantity
79
+ end
80
+ ```
81
+
82
+ ## Type Inference
83
+
84
+ The type system automatically infers appropriate types for broadcasted operations:
85
+
86
+ - `input.items.price` (float array) → inferred as `:float` per element
87
+ - `input.items.price * input.items.quantity` → element-wise `:float` result
88
+ - `fn(:sum, input.items.price)` → scalar `:float` result
89
+
90
+ ## Implementation Details
91
+
92
+ ### Parser Layer
93
+ - **InputFieldProxy** - Handles `input.field.subfield...` with path building
94
+ - **InputElementReference** - AST node representing array field access paths
95
+
96
+ ### Analysis Layer
97
+ - **BroadcastDetector** - Identifies vectorized vs scalar operations
98
+ - **TypeInferencer** - Infers types for array element access patterns
99
+
100
+ ### Compilation Layer
101
+ - **Automatic Dispatch** - Maps element-wise operations to array map functions
102
+ - **Reduction Detection** - Converts aggregation functions to array reduce operations
103
+
104
+ ## Usage Patterns
105
+
106
+ ### Element-wise Operations
107
+ ```ruby
108
+ # All of these broadcast element-wise
109
+ value :discounted_prices, input.items.price * 0.9
110
+ trait :expensive, (input.items.price > 100.0)
111
+ value :categories, input.items.category
112
+ ```
113
+
114
+ ### Aggregation Operations
115
+ ```ruby
116
+ # These consume arrays to produce scalars
117
+ value :item_count, fn(:size, input.items)
118
+ value :total_price, fn(:sum, input.items.price)
119
+ value :has_expensive, fn(:any?, expensive)
120
+ ```
121
+
122
+ ### Mixed Operations
123
+ ```ruby
124
+ # Element-wise computation followed by aggregation
125
+ value :line_totals, input.items.price * input.items.quantity
126
+ value :order_total, fn(:sum, line_totals)
127
+ value :avg_line_total, fn(:avg, line_totals)
128
+ ```
129
+
130
+ ## Error Handling
131
+
132
+ ### Dimension Mismatch Detection
133
+
134
+ Array broadcasting operations are only valid within the same array source. Attempting to broadcast across different arrays generates detailed error messages:
135
+
136
+ ```ruby
137
+ schema do
138
+ input do
139
+ array :items do
140
+ string :name
141
+ end
142
+ array :logs do
143
+ string :user_name
144
+ end
145
+ end
146
+
147
+ # This will generate a dimension mismatch error
148
+ trait :same_name, input.items.name == input.logs.user_name
149
+ end
150
+
151
+ # Error:
152
+ # Cannot broadcast operation across arrays from different sources: items, logs.
153
+ # Problem: Multiple operands are arrays from different sources:
154
+ # - Operand 1 resolves to array(string) from array 'items'
155
+ # - Operand 2 resolves to array(string) from array 'logs'
156
+ # Direct operations on arrays from different sources is ambiguous and not supported.
157
+ # Vectorized operations can only work on fields from the same array input.
158
+ ```
159
+
160
+ The error messages provide:
161
+ - **Quick Summary**: Identifies the conflicting array sources
162
+ - **Type Information**: Shows the resolved types of each operand
163
+ - **Clear Explanation**: Why the operation is ambiguous and not supported
164
+
165
+ ## Performance Characteristics
166
+
167
+ - **Single Pass** - Each array is traversed once per computation chain
168
+ - **Lazy Evaluation** - Operations are composed into efficient pipelines
169
+ - **Memory Efficient** - No intermediate array allocations for simple operations
170
+ - **Type Safe** - Full compile-time type checking for array element operations
@@ -0,0 +1,42 @@
1
+ # Input Declarations
2
+
3
+ Declares expected inputs with types and domain constraints, separating input metadata from business logic.
4
+
5
+ ## Declaration Syntax
6
+
7
+ ```ruby
8
+ schema do
9
+ input do
10
+ string :customer_name
11
+ integer :age, domain: 18..120
12
+ float :balance, domain: 0.0..Float::INFINITY
13
+ boolean :verified
14
+ array :tags, elem: { type: :string }
15
+ hash :metadata, key: { type: :string }, val: { type: :any }
16
+ any :flexible
17
+ end
18
+
19
+ trait :adult, (input.age >= 18)
20
+ value :status, input.verified ? "verified" : "pending"
21
+ end
22
+ ```
23
+
24
+ ## Domain Constraints
25
+
26
+ **Validation occurs at runtime:**
27
+ ```ruby
28
+ schema.from(credit_score: 900) # Domain: 300..850
29
+ # => InputValidationError: Field :credit_score value 900 is outside domain 300..850
30
+ ```
31
+
32
+ **Constraint types:**
33
+ - Range domains: `domain: 18..120`
34
+ - Array domains: `domain: %w[active inactive]`
35
+ - Regex domains: `domain: /^[a-zA-Z]+$/`
36
+
37
+ ## Validation Process
38
+
39
+ - Input data validated against declared field metadata
40
+ - Type validation checks value matches declared type
41
+ - Domain validation checks value satisfies constraints
42
+ - Detailed error messages for violations
@@ -0,0 +1,16 @@
1
+ # Performance
2
+
3
+ TODO: Add benchmark data
4
+
5
+ Processes large schemas with optimized algorithms for analysis, compilation, and execution.
6
+
7
+ ## Execution Model
8
+
9
+ **Compilation:**
10
+ - Each expression compiled to executable lambda
11
+ - Direct function calls for operations
12
+
13
+ **Runtime:**
14
+ - Result caching to avoid recomputation
15
+ - Selective evaluation: only requested keys computed
16
+ - Direct lambda invocation
@@ -28,7 +28,7 @@ module FederalTaxCalculator
28
28
  schema do
29
29
  input do
30
30
  float :income
31
- string :filing_status, domain: %(single married_joint married_separate head_of_household)
31
+ string :filing_status, domain: %w[single married_joint married_separate head_of_household]
32
32
  end
33
33
 
34
34
  # ── standard deduction table ───────────────────────────────────────
@@ -59,7 +59,7 @@ module FederalTaxCalculator
59
59
 
60
60
  value :fed_tax, fed_calc[0]
61
61
  value :fed_marginal, fed_calc[1]
62
- value :fed_eff, fed_tax / f[input.income, 1.0].max
62
+ value :fed_eff, fed_tax / [input.income, 1.0].max
63
63
 
64
64
  # ── FICA (employee share) ─────────────────────────────────────────────
65
65
  value :ss_wage_base, 168_600.0
@@ -96,10 +96,11 @@ module FederalTaxCalculator
96
96
  end
97
97
  end
98
98
 
99
- def calculate_tax(calculator, income: 1_000_000, status: "single")
99
+ def print_tax_summary(args)
100
+ r = FederalTaxCalculator.from(args)
100
101
  puts "\n=== 2024 U.S. Income‑Tax Example ==="
101
- printf "Income: $%0.2f\n", income
102
- puts "Filing status: #{status}\n\n"
102
+ printf "Income: $%0.2f\n", args[:income]
103
+ puts "Filing status: #{args[:filing_status]}\n\n"
103
104
 
104
105
  puts "Federal tax: $#{r[:fed_tax].round(2)} (#{(r[:fed_eff] * 100).round(2)}% effective)"
105
106
  puts "FICA tax: $#{r[:fica_tax].round(2)} (#{(r[:fica_eff] * 100).round(2)}% effective)"
@@ -107,4 +108,8 @@ def calculate_tax(calculator, income: 1_000_000, status: "single")
107
108
  puts "After-tax income: $#{r[:after_tax].round(2)}"
108
109
  end
109
110
 
110
- calculate_tax(FederalTaxCalculator, income: 1_000_000, status: "single")
111
+
112
+ input = { income: 1_000_000,
113
+ filing_status: "single"
114
+ }
115
+ print_tax_summary(input)
@@ -23,7 +23,7 @@ module Kumi
23
23
  return node.value if node.is_a?(Literal)
24
24
 
25
25
  result = case node
26
- when Binding then evaluate_binding(node, visited)
26
+ when DeclarationReference then evaluate_binding(node, visited)
27
27
  when CallExpression then evaluate_call_expression(node, visited)
28
28
  else :unknown
29
29
  end