about summary refs log tree commit diff stats
path: root/js/baba-yaga/scratch/baba/with.baba
blob: 05de15040a552363b8b9bf2e523ae93ebc7b30fc (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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// with.baba — Dev playground and implementation plan for header `with` locals
//
// This file documents the proposed `with` and `with rec` header clauses for
// local bindings inside function bodies. It contains:
// - Syntax and semantics
// - Typing rules (locals treated like globals)
// - Semicolon policy
// - Examples (untyped, typed, shadowing, with `when`, mutual recursion)
// - Error cases
// - Implementation plan (lexer, parser, interpreter, tests)
//
// NOTE: All examples are commented out so this file remains parseable until the
// feature is implemented.

// -----------------------------------------------------------------------------
// Syntax
//
// Function declaration (untyped params):
//   name : params -> with ( headerEntries ) -> body;
//
// Function declaration (typed params + return type):
//   name : (x: T, y: U) -> R -> with ( headerEntries ) -> body;
//
// Header entries inside `with ( ... )` are separated by semicolons `;` and may
// end with an optional trailing semicolon.
// Each entry is either:
//   - Type declaration (same style as global):
//       ident Type;
//   - Assignment (value):
//       ident : expression;
//
// Mutually recursive locals use `with rec` (or `with recursion`) to pre-bind:
//   name : args -> ...
//     with rec (
//       f : a -> ... g ...;
//       g : b -> ... f ...;
//     ) ->
//       body;
//
// Soft keywords:
// - `with` and `rec` are recognized only in the function-header position
//   (immediately after an arrow). Elsewhere they remain plain identifiers.

// -----------------------------------------------------------------------------
// Semantics
//
// with (non-rec):
// - Create an inner scope layered over the function call scope.
// - Process header entries left-to-right:
//   - Type decl: record `name -> Type` in the with-scope types map.
//   - Assignment: evaluate expression in the current with-scope, then
//     if a type was declared for `name`, validate using the existing runtime
//     type lattice (Int ⊂ Float ⊂ Number). Bind `name` to the result.
// - Evaluate `body` in that inner scope; discard the scope after.
// - No forward references among assignments.
// - Shadowing permitted (locals shadow params/globals).
//
// with rec (mutual recursion):
// - Pre-bind all names in the header to placeholders in the with-scope.
// - Evaluate each RHS in order and assign into the same with-scope.
// - Restrict RHS to functions (to avoid non-lazy cycles). If non-function is
//   assigned under `rec`, throw a clear error.
// - Body executes after all bindings are assigned.

// -----------------------------------------------------------------------------
// Semicolons
// - Inside `with ( ... )`: semicolons separate entries; trailing `;` allowed.
// - Between header and body: only the arrow `->`.
// - After `body`: same as today at top-level (semicolon optional/tolerated).

// -----------------------------------------------------------------------------
// Executable Examples

// 1) Basic locals (untyped)
addMul : x y ->
  with (inc : x + 1; prod : inc * y;) ->
    inc + prod;
addMulResult : addMul 2 5;

// 2) Quadratic roots (untyped)
quadraticRoots : a b c ->
  with (
    disc : b * b - 4 * a * c;
    sqrtDisc : math.sqrt disc;
    denom : 2 * a;
  ) ->
    { r1: (-b + sqrtDisc) / denom, r2: (-b - sqrtDisc) / denom };
roots : quadraticRoots 1 -3 2;

// 3) Typed params + typed locals (global-like declarations inside header)
sumNext : (x: Int, y: Int) -> Int ->
  with (
    nextX Int; nextY Int;
    nextX : x + 1;
    nextY : y + 1;
  ) ->
    nextX + nextY;
sumNextResult : sumNext 2 3;

// 4) Shadowing and ordering
shadow : x ->
  with (x : x + 1; y : x * 2) ->
    x + y;
shadowResult : shadow 3;

// 5) With + when
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";
classify0 : classify 0;
classify50 : classify 50;
classify200 : classify 200;

// Multi-discriminant with local
bucket : x y ->
  with (t : x + y) ->
    when x t is
      0 0 then "origin"
      _ _ then "other";
bucketResult : bucket 0 0;

// 6) Result + with + when
safeDivide : x y ->
  with (zero : 0) ->
    when y is
      0 then Err "Division by zero"
      _ then Ok (x / y);

useDivide : a b ->
  with (fallback Int; fallback : 0) ->
    when (safeDivide a b) is
      Ok v  then v
      Err _ then fallback;
divOk : useDivide 10 2;
divErr : useDivide 10 0;

// 7) with rec — mutual recursion among 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 };
rEvenOdd : isEvenOdd 0;

// Sample I/O to visualize outputs when running this file directly
io.out addMulResult;
io.out roots;
io.out sumNextResult;
io.out shadowResult;
io.out classify0 classify50 classify200;
io.out bucketResult;
io.out divOk divErr;
io.out rEvenOdd;

// -----------------------------------------------------------------------------
// Error cases (intended)
// - Non-rec forward reference among assignments in `with`: error.
// - In `with rec`, assignment RHS must evaluate to a function: error otherwise.
// - Type mismatch for a typed local: reuse existing error messaging similar to
//   function parameter/return mismatches.

// -----------------------------------------------------------------------------
// Implementation plan
// 1) Lexer (none/minimal):
//    - Keep `with` / `rec` as IDENTIFIER tokens; treat as soft keywords in parser.
//
// 2) Parser (parser.js):
//    - After params and optional return type, detect: IDENT('with') [IDENT('rec'|'recursion')] LPAREN ... RPAREN ARROW
//    - Parse header entries list:
//        entry := IDENT TYPE ';'  |  IDENT ':' expression ';'
//        allow repeated entries and optional trailing ';'
//    - AST: augment FunctionDeclaration/CurriedFunctionDeclaration body with optional
//      WithHeader { recursive: boolean, entries: Array< TypeDecl | Assign > }
//      where TypeDecl { name, typeAnnotation }, Assign { name, expr }
//    - Ensure support both in top-level function and nested curried bodies.
//
// 3) Interpreter (interpreter.js):
//    - When invoking a function with WithHeader:
//        - Create with-scope Map layered over call-scope; maintain withTypes Map.
//        - If recursive:
//            - Pre-bind all names to undefined in with-scope.
//            - Evaluate each Assign RHS with with-scope in effect; verify RHS is Function; set binding.
//          Else:
//            - Process entries left-to-right: on TypeDecl store in withTypes; on Assign evaluate and bind.
//        - On each Assign, if a type exists in withTypes, validate via existing lattice.
//        - Evaluate body in with-scope; finally restore original scope.
//
// 4) Tests (tests/*.test.js):
//    - parser-with-header.test.js (new): parsing of with/with rec, typed locals, trailing semicolon, shadowing, disallow forward refs.
//    - interpreter-with-header.test.js (new):
//        * Basic untyped locals
//        * Typed locals pass and fail
//        * With + when interactions
//        * Shadowing behavior
//        * with rec mutual recursion (success), non-function RHS (error)
//    - Back-compat pass: ensure existing tests still green.
//
// 5) Docs:
//    - Update README and ref.txt with the new form, semicolon policy, and examples.