Skip to content

stdlib: complete the Seq module - #44

Merged
simontreanor merged 1 commit into
mainfrom
feat/stdlib-seq-sweep
Jul 31, 2026
Merged

stdlib: complete the Seq module#44
simontreanor merged 1 commit into
mainfrom
feat/stdlib-seq-sweep

Conversation

@simontreanor

Copy link
Copy Markdown
Owner

Dogfooding finding #5, PR 2 of 5 (see #34). Follows #42 (List) and #43 (member docs).

Seq carried 7 members against List's 51, which made the lazy half of the library the one you left for a list plus a conversion. This adds 22, and the single-pass caveat turned out to be the organising idea rather than a footnote — every member's hover line now says which half it is in.

Lazy — a new sequence, nothing pulled yet: drop takeWhile dropWhile concat flatten collect zip indexed distinct pairwise init initInfinite unfold. Most route to Python's own lazy machinery (itertools.islice/takewhile/dropwhile/chain/pairwise/count), so a chain of them stays lazy. distinct and unfold are emitted generators, since they carry state while staying lazy.

Consuming — a Python iterator is single-pass, so what these pull is gone: head isEmpty len exists forall contains sum iter find. Several short-circuit, consuming only as far as the answer.

initInfinite and unfold have no list behind them, which is the reason the lazy module exists:

Seq.initInfinite (fun i -> i * 2) |> Seq.take 5 |> Seq.toList   # [0, 2, 4, 6, 8]
Seq.unfold (fun s -> if s > 100 then None else Some (s, s * 2)) 1 |> Seq.take 4   # [1, 2, 4, 8]
Seq.find (fun x -> x > 6) (Seq.initInfinite (fun i -> i * 2))   # Some(8)

Two end-to-end tests build endless sequences deliberately. Neither terminates if a member that claims to be lazy quietly forces its input, so they check the laziness itself rather than asserting on emitted text.

Naming follows List, not F#: drop rather than skip, and concat appends while flatten flattens — the same divergence already taken for List, kept consistent so the two modules read the same way.

Documentation carries the cost, which matters more here than for List: that Seq.len walks the whole sequence and never returns on an endless one is exactly what a reader needs before calling it, and that is now in hover rather than in DESIGN prose.

Tests: 7 typecheck cases (lazy members stay in Seq, Seq and List members do not mix, unfold must answer Option, iter carries its effect), 3 lowering assertions (routes to the lazy machinery, distinct yields rather than building a list, unfold's loop), and 3 end-to-end programs including the two endless ones. Full suite, clippy and fmt clean.

Seq had 7 members against List's 51, which made the lazy half of the
library the one you left for a list plus a conversion. This adds 22, and
the single-pass caveat decides how they are grouped.

Lazy, answering a new sequence with nothing pulled yet: drop, takeWhile,
dropWhile, concat, flatten, collect, zip, indexed, distinct, pairwise,
init, initInfinite, unfold. Most route to Python's own lazy machinery
(itertools.islice/takewhile/dropwhile/chain/pairwise/count), so a chain of
them stays lazy; distinct and unfold are emitted generators, since they
carry state while staying lazy.

Consuming, where a Python iterator being single-pass means what they pull
is gone: head, isEmpty, len, exists, forall, contains, sum, iter, find.
Several short-circuit, so they consume only as far as the answer.

initInfinite and unfold are the members with no list behind them, which is
the reason the lazy module exists at all. Two end-to-end tests pin that
down by building endless sequences and taking from them: neither terminates
if a member that claims to be lazy quietly forces its input.

Naming follows List rather than F#: drop not skip, and concat appends where
flatten flattens. Every member's hover line says which half it is in, and
what it costs to ask — that Seq.len walks the whole thing and never returns
on an endless sequence is exactly what a reader needs before calling it.
@simontreanor
simontreanor merged commit 3c57e21 into main Jul 31, 2026
11 checks passed
@simontreanor
simontreanor deleted the feat/stdlib-seq-sweep branch July 31, 2026 15:55
simontreanor added a commit that referenced this pull request Jul 31, 2026
Two dogfooding reports from real programs, and the standard-library sweep
they triggered.

Language:

* a `type` declaration can name an imported type, bare or module-qualified
  (#36) — the one gap that changed a program's architecture rather than its
  phrasing, forcing two modules into one file
* field access resolves from the base's type when it is known, so two
  records may share a field name without prefixes (#37)
* parameters destructure: tuples (#38), records (#40), and `_`
* a direct self tail call lowers to a loop, so an interactive turn loop no
  longer walks the stack (#39, #41)

Standard library — about 115 new members, taking every module to the F#
core set: List (#42), Seq (#44), Set and Map (#46), String (#47), Option
and Result (#48), then a member-by-member FSharp.Core audit (#51). Every
built-in member now carries a one-line description and its complexity in
hover and completion (#43, #49), enforced by tests.

Fixes:

* `pyfun run` on a single file gives the program its own stdin, so an
  interactive program is runnable by the command whose job is running
  programs (#35)
* a partially applied lambda closes over its argument instead of being
  wrapped, so `List.map ((+) 2)` emits `lambda b: 2 + b` (#52)
* every multi-argument callback's scheme put the effect variable on the
  wrong arrows, so `List.fold` could never accept an effectful folder (#51)
* `Seq.empty` lowered to a bare `iter()`, a TypeError (#51)

One source-incompatible change, which is why this is 0.4.0 and not 0.3.1:
a dotted `extern` target whose module prefix cannot be decided from the
text is now a compile error naming the `extern import` to add (#50).
`sys.stdout.flush` used to emit `import sys.stdout` and fail at runtime;
declaring `extern import sys` fixes it.
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.

1 participant