Skip to content

Commit c1db14e

Browse files
author
Eric Hartford
committed
progress
1 parent c636b16 commit c1db14e

50 files changed

Lines changed: 2631 additions & 1085 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/fix_more_annoyances.md

Lines changed: 330 additions & 26 deletions
Large diffs are not rendered by default.

docs/implicit_it.md

Lines changed: 89 additions & 190 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ is equivalent to:
1616
items |> filter(|n| n % 2 == 0)
1717
```
1818

19-
---
19+
## Design
2020

21-
## Syntax
21+
### Syntax
2222

2323
Any expression passed in a closure position that references `it`
2424
and has no explicit parameter list is treated as an implicit
@@ -36,22 +36,16 @@ single-parameter closure.
3636
// 3. No explicit parameter list is present
3737
```
3838

39-
---
40-
41-
## Semantics
42-
4339
### Type Inference
4440

4541
`it` receives its type from the expected parameter type at the call
4642
site, exactly as explicit closure parameters do today.
4743

4844
```
49-
// filter expects fn(i32) -> bool
50-
// therefore it: i32
45+
// filter expects fn(i32) -> bool → it: i32
5146
items |> filter(it % 2 == 0)
5247
53-
// map expects fn(User) -> str
54-
// therefore it: User
48+
// map expects fn(User) -> str → it: User
5549
users |> map(it.name)
5650
```
5751

@@ -74,54 +68,6 @@ items |> map(it.children |> filter(it.active))
7468
// use explicit parameter: |c| c.active
7569
```
7670

77-
### Where `it` Is Available
78-
79-
`it` is available anywhere a single-parameter closure is expected:
80-
81-
```
82-
// Pipeline functions
83-
items |> filter(it > 0)
84-
items |> map(it.name)
85-
items |> any(it.active)
86-
items |> all(it > threshold)
87-
items |> find(it.id == target_id)
88-
items |> count(it.is_valid())
89-
items |> reduce(0, |acc, x| acc + x) // multi-param: explicit
90-
91-
// Method calls
92-
items.filter(it > 0)
93-
items.map(it.name)
94-
items.sort_by(it.age)
95-
96-
// Standalone closures assigned to variables
97-
let is_even = (it % 2 == 0) // type inferred from usage
98-
let double = (it * 2)
99-
100-
// Function arguments
101-
let result = retry(3, it + 1) // ERROR if fn expects 2+ params
102-
```
103-
104-
### Where `it` Is NOT Available
105-
106-
- **Multi-parameter closures.** If the expected function type has more
107-
than one parameter, `it` is not available. Use explicit parameters.
108-
109-
```
110-
// reduce expects fn(Acc, T) -> Acc — two params, must be explicit
111-
items |> reduce(0, |acc, x| acc + x)
112-
113-
// zip_with expects fn(A, B) -> C — two params, must be explicit
114-
zip_with(xs, ys, |a, b| a + b)
115-
```
116-
117-
- **Ambiguous closure position.** If the compiler cannot determine
118-
that the expression is in a closure position (e.g., it's a bare
119-
expression not passed to a function), `it` is a normal identifier
120-
lookup and follows standard resolution.
121-
122-
- **Nested implicit closures.** As described above, the inner closure
123-
must use explicit parameters.
124-
12571
### `it` Is A Reserved Keyword
12672

12773
`it` is a keyword, the same as `if`, `for`, `match`, `fn`. It cannot
@@ -137,33 +83,32 @@ fn it(): // ERROR: `it` is a reserved keyword
13783
items |> filter(it > 0) // implicit closure parameter
13884
```
13985

140-
This eliminates all shadowing ambiguity. `it` always means exactly
141-
one thing: the implicit single-parameter closure reference.
142-
143-
### Interaction With `_`
86+
### `_` Is Not A Closure Placeholder
14487

145-
`_` remains the discard/wildcard symbol. `it` is the implicit parameter.
146-
They do not overlap.
88+
The `_` placeholder syntax (e.g., `items |> filter(_.age > 21)`) was
89+
previously specified as a closure shorthand. This has been removed from
90+
the language. `it` is the sole implicit closure parameter. `_` is
91+
exclusively a discard/wildcard:
14792

148-
```
149-
// _ discards a value
150-
let _ = expensive_call()
151-
152-
// it is the implicit parameter
153-
items |> filter(it > 0)
93+
- `_` in patterns: wildcard/discard (unchanged)
94+
- `_` in partial application: placeholder for curried argument (unchanged)
95+
- `_.field` as closure shorthand: **REMOVED** — use `it.field` instead
96+
- `it` in expressions: implicit single closure parameter (the one way)
15497

155-
// In match arms, _ is wildcard, it is not special
156-
match value
157-
Some(x) -> x
158-
_ -> 0
159-
```
98+
### Where `it` Is NOT Available
16099

161-
---
100+
- **Multi-parameter closures.** If the expected function type has more
101+
than one parameter, `it` is not available. Use explicit parameters.
102+
- **Ambiguous closure position.** If the compiler cannot determine
103+
that the expression is in a closure position, `it` is a normal
104+
identifier lookup and follows standard resolution.
105+
- **Nested implicit closures.** The inner closure must use explicit
106+
parameters.
162107

163-
## Desugaring
108+
### Desugaring
164109

165110
The compiler desugars implicit closures early, during parsing or
166-
immediately after. The transformation is:
111+
immediately after:
167112

168113
```
169114
// Source
@@ -175,10 +120,7 @@ items |> filter(|__it| __it % 2 == 0)
175120

176121
The desugared parameter name is internal. The user always writes `it`.
177122

178-
### Detection Algorithm
179-
180-
When the compiler encounters an expression in a closure-expected
181-
position:
123+
**Detection algorithm:**
182124

183125
1. Walk the expression tree.
184126
2. If `it` appears anywhere in the expression:
@@ -193,110 +135,9 @@ Since `it` is a reserved keyword, it can never appear as a local
193135
variable or other binding. Any occurrence of `it` in an expression
194136
unambiguously signals an implicit closure.
195137

196-
---
197-
198-
## Method Access Shorthand
199-
200-
A common pattern is accessing a field or calling a method:
201-
202-
```
203-
users |> map(it.name)
204-
users |> filter(it.is_active())
205-
users |> sort_by(it.age)
206-
```
138+
### Error Messages
207139

208-
These desugar to:
209-
210-
```
211-
users |> map(|__it| __it.name)
212-
users |> filter(|__it| __it.is_active())
213-
users |> sort_by(|__it| __it.age)
214-
```
215-
216-
---
217-
218-
## Chaining
219-
220-
`it` works naturally in chained expressions:
221-
222-
```
223-
users
224-
|> filter(it.age >= 18)
225-
|> map(it.name |> uppercase)
226-
|> filter(it |> starts_with("A"))
227-
```
228-
229-
Each `it` in each pipeline step refers to that step's closure
230-
parameter. There is no ambiguity because each step is a separate
231-
closure position.
232-
233-
---
234-
235-
## Comparison With Explicit Closures
236-
237-
| Pattern | Explicit | Implicit |
238-
|---|---|---|
239-
| Simple predicate | `\|n\| n > 0` | `it > 0` |
240-
| Field access | `\|u\| u.name` | `it.name` |
241-
| Method call | `\|u\| u.is_active()` | `it.is_active()` |
242-
| Arithmetic | `\|n\| n * 2 + 1` | `it * 2 + 1` |
243-
| Two params | `\|a, b\| a + b` | not available |
244-
| Nested closure | `\|u\| u.items.filter(\|i\| i.ok)` | `it.items.filter(\|i\| i.ok)` |
245-
246-
---
247-
248-
## Examples
249-
250-
### Idiomatic With with `it`
251-
252-
```
253-
// Filter and sum
254-
let total = items |> filter(it > 0) |> sum
255-
256-
// Extract field
257-
let names = users |> map(it.name) |> collect[Vec]
258-
259-
// Chain predicates
260-
let results = entries
261-
|> filter(it.active)
262-
|> filter(it.score > threshold)
263-
|> map(it.name)
264-
265-
// Sort
266-
let sorted = users |> sort_by(it.age)
267-
268-
// Find
269-
let admin = users |> find(it.role == .Admin)
270-
271-
// Any / All
272-
let has_errors = results |> any(it.is_err())
273-
let all_valid = inputs |> all(it.len() > 0)
274-
275-
// String processing
276-
let cleaned = lines
277-
|> map(it |> trim)
278-
|> filter(it.len() > 0)
279-
|> filter(it |> starts_with("#") |> not)
280-
281-
// Nested — inner must be explicit
282-
let active_children = groups
283-
|> map(it.members |> filter(|m| m.active) |> count)
284-
```
285-
286-
### The Landing Page Example
287-
288-
```
289-
fn main:
290-
let sum = read_file("nums.txt")?
291-
|> lines |> map(parse[i32]) |> filter(it % 2 == 0) |> sum
292-
println("Sum of evens: {sum}")
293-
```
294-
295-
---
296-
297-
## Error Messages
298-
299-
### Nested implicit closure
140+
Three dedicated error codes:
300141

301142
```
302143
error[E0901]: nested implicit closure is ambiguous
@@ -309,8 +150,6 @@ error[E0901]: nested implicit closure is ambiguous
309150
= suggestion: groups |> map(it.items |> filter(|x| x.active))
310151
```
311152

312-
### Wrong arity
313-
314153
```
315154
error[E0902]: `it` used in context expecting 2 parameters
316155
--> src/main.w:8:30
@@ -321,8 +160,6 @@ error[E0902]: `it` used in context expecting 2 parameters
321160
= help: use explicit parameters: |acc, x| acc + x
322161
```
323162

324-
### `it` used as identifier
325-
326163
```
327164
error[E0903]: `it` is a reserved keyword
328165
--> src/main.w:3:9
@@ -332,4 +169,66 @@ error[E0903]: `it` is a reserved keyword
332169
|
333170
= note: `it` is the implicit closure parameter keyword
334171
= help: choose a different name
335-
```
172+
```
173+
174+
---
175+
176+
## Implementation Checklist
177+
178+
### 1. Spec (`docs/with-specification.md`)
179+
- [x] Remove `_.field` closure shorthand from spec. `it.field` is the one way.
180+
Done: `_.active``it.active` in §feature summary and intro.md;
181+
"Placeholder syntax" → "Implicit `it` parameter (see §9.3.1)";
182+
added full §9.3.1 subsection defining `it` keyword, scoping,
183+
nested prohibition, and clarifying `_` is not a closure placeholder.
184+
- [ ] Add `it` to the keyword list in the lexical grammar section.
185+
- [ ] Add the implicit closure grammar production to §9.3 (closure expressions).
186+
- [ ] Document the desugaring algorithm in the spec.
187+
- [ ] Add error code definitions for E0901, E0902, E0903.
188+
189+
### 2. Tokenizer / Parser (`src/Token.w`, `src/Parser.w`)
190+
- [ ] Add `it` as a reserved keyword token (e.g., `TK_IT`).
191+
- [ ] Reject `it` as an identifier in let bindings, fn declarations, parameter lists, and field names.
192+
- [ ] Detect implicit closure form: when parsing a call argument in a closure-expected position, check if the expression contains `it` with no explicit `|...|` parameter list.
193+
- [ ] Desugar to an explicit closure AST node with a synthetic parameter bound to `it`.
194+
- [ ] Handle nested detection: if an implicit closure body contains another `it` reference at a nested closure-expected position, emit E0901.
195+
196+
### 3. Sema (`src/Sema.w`)
197+
- [ ] Type-check desugared implicit closures the same as explicit closures (no special path needed if desugaring is complete before sema).
198+
- [ ] Validate arity: if `it` appears in a context expecting `fn(A, B, ...) -> R` with arity != 1, emit E0902.
199+
- [ ] Validate keyword: if `it` appears as a binding name (let, fn, param, field), emit E0903.
200+
201+
### 4. Codegen
202+
- [ ] No changes expected — implicit closures are desugared to standard closure AST nodes before codegen. Verify this holds.
203+
204+
### 5. Docs
205+
- [ ] Update `docs/with-idiomatic-guide.md` with `it` usage examples and guidance (when to use `it` vs explicit params).
206+
- [ ] Update `docs/with-migration-guide.md` with mappings from Rust closure patterns to `it`.
207+
- [ ] Update `docs/intro.md` examples to use `it` where appropriate (partially done: `filter(it.active)` already there).
208+
209+
### 6. Tests
210+
- [ ] Parser test: `it > 0` in closure position desugars to `|__it| __it > 0`.
211+
- [ ] Parser test: `it.name` field access desugars correctly.
212+
- [ ] Parser test: `it.is_active()` method call desugars correctly.
213+
- [ ] Parser test: nested `it` in nested closure position → E0901 error.
214+
- [ ] Parser test: `it` in multi-param context → E0902 error.
215+
- [ ] Parser test: `let it = 42` → E0903 error.
216+
- [ ] Parser test: `fn it():` → E0903 error.
217+
- [ ] Parser test: `|it| it + 1` → E0903 error (cannot use keyword as param name).
218+
- [ ] Sema test: `it` receives correct type from expected parameter type.
219+
- [ ] Sema test: chained pipelines — each `it` gets its step's type.
220+
- [ ] Integration test: `items |> filter(it > 0) |> map(it * 2)` produces correct output.
221+
- [ ] Integration test: explicit inner closure with implicit outer works (`it.children |> filter(|c| c.active)`).
222+
- [ ] Integration test: `it` works with method call syntax (`items.filter(it > 0)`).
223+
224+
### 7. Bootstrap Compatibility
225+
- [ ] `it` implementation goes into the self-host compiler only. Bootstrap (Zig) is unchanged.
226+
- [ ] Self-host source (`src/`) must NOT use `it` syntax — bootstrap cannot compile it.
227+
- [ ] Self-host compiler must be able to compile user code that uses `it`.
228+
229+
### 8. Validation Gates
230+
- [ ] All parser tests pass.
231+
- [ ] All sema tests pass.
232+
- [ ] All integration tests pass.
233+
- [ ] Stage3 chain passes with `it` support compiled in.
234+
- [ ] No regressions in existing closure tests.

docs/intro.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ It appears in four forms:
6565

6666
```
6767
with lock.read() as data:
68-
data.iter() |> filter(_.active) |> count()
68+
data.iter() |> filter(it.active) |> count()
6969
```
7070

7171
The lock is held for exactly the block. The compiler knows the type implements `Scoped`, dispatches through the guard automatically. No keywords, no ceremony — the type tells the compiler everything.

0 commit comments

Comments
 (0)