stdlib: state the cost on every traversing member, and test it - #49
Merged
Conversation
The sweep left a handful of members without a complexity: List.range (which builds a list where Seq.range does not), iter, init, replicate and chunkBySize, plus five Seq members whose lazy-or-consuming half was implied rather than said. The test is the durable part. Every member of List, Seq, Set, Map and String must now state a cost, in big-O or in what it consumes, with a four-name exemption list for the genuine constants. Option and Result hold one value and are uniformly O(1), and Format, Decode and the globals are not traversals, so those modules are not held to it, which the test says in its own documentation so the omission reads as a decision.
Merged
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to the sweep, from the question of whether the big-O notation actually covers everything the last few PRs added. It did not.
I wrote a checker over
MEMBER_DOCSand found three groups.Map/Setwere already covered.Stringwas missing costs almost entirely, which #47 fixed on its way in. This PR closes the rest:List.range— O(n), and worth saying because it builds the list whereSeq.rangedoes not.List.iter,List.init,List.replicate,List.chunkBySize— O(n), simply missed.Seq.fold,Seq.toList,Seq.ofList,Seq.initInfinite,Seq.unfold— each had its lazy-or-consuming half implied rather than said.Seq.ofListis now explicitly O(1) with no copy, which is the one a reader might assume otherwise.The durable part is the test. Every member of
List,Seq,Set,MapandStringmust state a cost — in big-O, or (forSeq) in what it consumes — with a four-name exemption list for the genuine constants:Set.empty,Map.empty,String.fromInt,String.fromFloat.Not held to it, deliberately:
OptionandResulthold one value and are uniformly O(1), so a complexity on all 24 of their members would be noise rather than information;Format,Decodeand the globals are not traversals either. That exemption is by module and is stated in the test's own documentation, so the omission reads as a decision rather than an oversight.This joins the three existing
MEMBER_DOCStests (every member documented, every entry real, every member has a signature), so the standard for a new stdlib member is now: it exists, it is documented, its documentation states its cost, and it has a signature.