Skip to content

ce: for items, Python-spelled, in every builder - #114

Merged
simontreanor merged 1 commit into
mainfrom
ce-for-items
Aug 30, 2026
Merged

ce: for items, Python-spelled, in every builder#114
simontreanor merged 1 commit into
mainfrom
ce-for-items

Conversation

@simontreanor

Copy link
Copy Markdown
Owner

Closes #103 (the for item; the diagnostic and docs-table halves shipped in #112; custom operations are recorded as turned down in ROADMAP item 15).

for target in source: in every builder

The Python spelling, consistent with match e: / case. for is a keyword only in item position inside CE braces (Pyfun has no loop statement elsewhere). The source is a List a or a Seq a (an unresolved source is taken to be a List), the target any irrefutable pattern (for (a, b) in pairs: unpacks in the Python header), and the body is a nested level of the same block: anything but return, which is rejected with a message saying the block's value comes after the loop.

  • seq lowers to Python's for statement, the idiomatic generator that yield! (List.map f xs) was standing in for.
  • async loops with awaits inside the async def.
  • result / option: a failed let! inside the loop short-circuits the whole block; the items after the loop follow the for statement.
  • User builders get F#'s For: B.for_ e (fun x -> body), combined with what follows like a yield. The issue's Html { } DSL now writes for r in rows: yield tr r.
let squares = seq {
  for x in [1, 2, 3]:
    let y = x * x
    yield y
  yield 100
}
def _pf_fn0():
    for x in [1, 2, 3]:
        y = x * x
        yield y
    yield 100

One layout rule

Braces carry no layout tokens, so the body is delimited by columns like every other item, with one Python-shaped rule: a body that begins on the same line as the : is exactly one item (for x in xs: yield x), and a longer body goes on indented lines. The pretty-printer keeps one-line CEs one-line and lays a block out on lines only when a for body has several items, so parse ∘ print stays idempotent (roundtrip tests added for both shapes, including nested loops). Inside CE braces for also stops an application, so yield a for c in cs: yield c is two items.

Elsewhere

CeItem::For is walked by the capture census, the fold pass's free-variable and binder collectors, the active-pattern arity scan, and the LSP resolver (the target is a nested scope, so go-to-definition and rename see it). DESIGN.md §8.1 gets the row and a paragraph; the internals table, lesson 13 (a for section in seq) and lesson 20 (for_ in the protocol) are updated; ROADMAP item 15 is closed.

Tests

Typecheck (every builder, the Seq string element type of a tuple target, return-in-loop and non-collection-source rejections), roundtrip (one-line and multi-line shapes), a parser unit test for the near-miss diagnostic, and one e2e program running all four builders. cargo test, cargo clippy --all-targets, cargo fmt --check and docs/verify_lessons.py are clean.

for target in source: runs its body once per element of a List or Seq
with the target bound, inside seq/async/result/option (Python's for
statement; a failed let! inside a result loop still short-circuits the
block) and user builders (B.for_ e (fun x -> body), combined with what
follows like a yield). for is a keyword only in item position inside
CE braces; a body on the same line as the colon is exactly one item, a
longer body goes on indented lines, and the pretty-printer lays such a
block out on lines. return inside a loop body is rejected. Lesson 13
and 20, DESIGN §8.1, the internals table and ROADMAP item 15 updated.
@simontreanor
simontreanor merged commit 2c078d1 into main Aug 30, 2026
16 checks passed
@simontreanor
simontreanor deleted the ce-for-items branch August 30, 2026 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A markup CE needs for (in seq too) and a decision on custom operations; the docs table stops at yield

1 participant