about summary refs log tree commit diff stats
path: root/js/scripting-lang/design/HISTORY/FUNCTION_COMPOSITION_PLAN.md
blob: 34ee728bc8971894be267b50e76674bf25de153b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# Function Composition & Currying Design Plan - REVISED

## Current Issue Analysis

### Problem Statement
The current function application implementation has a fundamental flaw for function composition:

```javascript
f g x  // Currently parsed as: apply(apply(f, g), x)
       // This fails because: apply(f, g) = NaN (f expects a number, not a function)
       // Then: apply(NaN, x) = Error
```

### Root Cause
1. **Left-associative parsing**: `f g x``(f g) x``apply(apply(f, g), x)`
2. **Non-curried functions**: Functions expect specific argument types, not other functions
3. **Missing composition semantics**: No built-in understanding of function composition

## Design Decision: Simplified Approach

### Option 1: Full Currying (Haskell-style) ❌
**Why not**: Major architectural change, breaks existing code, complex implementation

### Option 2: Explicit Composition Only ✅ **RECOMMENDED**
**Why this is better**:
- **No ambiguity**: `f via g x` is always composition, `f g x` is always left-associative application
- **Backward compatible**: All existing code works unchanged
- **Clear intent**: Explicit composition makes code more readable
- **No complex detection**: No need for runtime type checking
- **Natural language**: `via` reads like English and is self-documenting

### Option 3: Hybrid Approach ❌
**Why not**: Overcomplicated, introduces ambiguity, harder to understand

## Recommended Solution: Explicit Composition Only

### 1. Keep Current Function Application
- `f x``apply(f, x)` (immediate application)
- `f g x``apply(apply(f, g), x)` (left-associative, as currently implemented)
- Functions remain non-curried by default
- Maintains current behavior for simple cases

### 2. Add Explicit Composition Keyword
- `f via g``compose(f, g)` (explicit composition)
- `f via g via h``compose(f, compose(g, h))` (right-associative)
- `f via g x``apply(compose(f, g), x)` (composition then application)
- Clear and explicit about intent

### 3. Fix and Enhance @ Operator
- `@f` → function reference (fix current parsing issues)
- `map(@f, [1,2,3])` → pass function as argument
- `when x is @f then ...` → pattern matching on functions
- Essential for higher-order programming

### 4. Enhanced Standard Library
- Improve `compose` function to handle multiple arguments
- Add `pipe` for left-to-right composition
- Add `curry` and `uncurry` utilities for when needed

## Implementation Plan

### Phase 1: Lexer Enhancement
- Add composition keyword (`via`)
- Fix `@` operator parsing issues
- Update token precedence

### Phase 2: Parser Enhancement
- Add `parseComposition()` function
- Fix `parsePrimary()` to handle `@` operator correctly
- Implement explicit composition parsing

### Phase 3: Standard Library Enhancement
- Improve `compose` function
- Add `pipe` function
- Add `curry`/`uncurry` utilities

### Phase 4: Testing & Validation
- Test all composition scenarios
- Ensure backward compatibility
- Performance testing

## Syntax Examples

### Current (Working)
```javascript
f : x -> x * 2;
g : x -> x + 1;

result1 : f x;        // apply(f, x) = 10
result2 : f (g x);    // apply(f, apply(g, x)) = 12
```

### Proposed (Enhanced)
```javascript
f : x -> x * 2;
g : x -> x + 1;

result1 : f x;        // apply(f, x) = 10
result2 : f via g x;  // apply(compose(f, g), x) = 12
result3 : pipe(f, g) x; // apply(pipe(f, g), x) = 12
result4 : @f;         // function reference to f
result5 : map(@f, [1,2,3]); // [2, 4, 6]

// Natural language examples
data : [1, 2, 3, 4, 5];
result6 : data via filter via map via reduce;  // Pipeline example
result7 : x via abs via double via add(10);    // Mathematical pipeline
```

## Why `via` is Better Than `.`

### 1. **Natural Language**
- `f via g x` reads like "f via g applied to x"
- `data via filter via map` reads like "data via filter via map"
- More intuitive for non-FP developers

### 2. **No Conflicts**
- No confusion with decimal numbers
- No conflict with object property access
- Won't interfere with existing syntax

### 3. **Clear Intent**
- Explicitly indicates composition
- Self-documenting code
- No ambiguity about what's happening

### 4. **Better Error Messages**
- "Expected function after 'via'" is clearer than "Expected function after '.'"
- More natural error reporting

### 5. **Accessibility**
- Lower learning curve
- No prior FP knowledge needed
- Intuitive for beginners

## Backward Compatibility

### Guaranteed to Work
- All existing function calls: `f x`
- All existing operator expressions: `x + y`
- All existing function definitions
- All existing when expressions
- All existing table operations

### New Features (Optional)
- Explicit composition: `f via g`
- Fixed function references: `@f`
- Enhanced standard library functions

## Why This Approach is Better

### 1. Simplicity
- No complex detection logic
- No runtime type checking
- Clear, predictable behavior

### 2. Clarity
- `f g x` always means `(f g) x`
- `f via g x` always means `f(g(x))`
- No ambiguity about intent

### 3. Familiarity
- `via` is intuitive and self-explanatory
- No mathematical notation to learn
- Easy to understand and teach

### 4. Flexibility
- Users can choose when to use composition
- No forced architectural changes
- Easy to extend later if needed

## Next Steps

1. **Implement Phase 1**: Add composition keyword to lexer, fix @ operator
2. **Implement Phase 2**: Add composition parsing to parser
3. **Implement Phase 3**: Enhance standard library
4. **Test thoroughly**: Ensure all existing code still works
5. **Document**: Update language documentation
6. **Examples**: Create comprehensive examples

## Success Criteria

- [ ] `f via g x` works correctly for function composition
- [ ] `@f` works correctly for function references
- [ ] All existing code continues to work unchanged
- [ ] Performance impact is minimal
- [ ] Error messages are clear and helpful
- [ ] Documentation is comprehensive

## Conclusion

The explicit composition approach using `via` is simpler, clearer, and more maintainable than the hybrid approach. It provides the functionality we need without the complexity and potential ambiguity of automatic detection. The `via` keyword makes the language more accessible and self-documenting, while maintaining all the power of functional composition. Combined with fixing the `@` operator, this gives us a powerful and clear functional programming language.