Seq[T] for What Is Produced Step by Step, and Par[T] Where the Steps Run at OnceStatus: Accepted
Date: September 15, 2026
Related: ADR-288 (the ledger’s type language, whose words
these join), ADR-024 (? is the absence of a claim),
ADR-277 D6 and D7 (sync and throws after a type, which a
step borrows), ADR-094 D2 and D4 (a value walked is a value
kept), ADR-025 D1 (a loop’s step can fail, and the function
declares it), Part II 12.6 (par_iter and the sync lambda),
ADR-172 D1 (pauses), ADR-235 (par_iter),
ADR-293 D16-D19 (what a sequence is as a whole, and what takes one)
The ledger’s type language has a name, type arguments, a variable, a view,
a tuple, a function type and ?. It has no word for what keys(),
chars(), io::lines() and xs.map fn … hand back: elements of a type,
produced one at a time when asked for, with no length until the end and
nothing laid out in memory. Without one those entries say -> ?, and
because a method is found through its receiver’s type, everything called on
such a value is unfound too.
A container is not this. A Vec[T] has its elements already, a length, and
an index; it is walked as often as one likes. A produced sequence is walked
once, and each step may do work: a step of io::lines() reads the pipe, so
it may pause and may fail, which Part I 3.3 already turns into a throws on
the function. What is missing is the type the property hangs on.
Seq[T] is a word of the ledger’s type language, and what a step may do stands after it as it stands after a function type.Changed by ADR-172 D1: a step that pauses says pauses; sync promises it does not, and neither word means nobody said.
Seq[T] is elements of T, produced step by step. sync, pauses and
throws after it say what one step may do, with ADR-277 D7’s
reading for throws: without it a step cannot fail.
[fn."HashMap::keys"]
signature = "[K, V](self: ref collections::HashMap[K, V]) -> Seq[ref K] sync sized"
[fn."io::lines"]
signature = "() -> Seq[String] pauses throws"
[fn."Seq::collect"]
signature = "[T](self: Seq[T]) -> Vec[T]"
A container keeps its own name. Vec[T], HashMap[K, V] and a range are
walked as a Seq by a for, and are not one.
Seq is consumed by walking it, and a second walk is refused.Changed by ADR-293 D18 and D19: every read of a name that holds a sequence takes it, not only a walk, and a range replays, so the refusal (NK2702) never applies to one.
for, collect, count, join, nth and every other method that walks
take the Seq by value: it is a value kept (ADR-094 D2), and a
use after it is refused in this compiler’s words — this sequence was
already walked. That is the whole of the once-only rule; no word carries
it. A container is walked by view (ADR-094 D4) and as often as
one likes.
Par[T] is what par_iter() hands back, and a lambda handed to it must be sync.Changed by ADR-235 D1 and D2: Vec::par_iter is a Par[ref T], and its lambda may neither pause (NK2209) nor change a name bound outside it (NK2107), at both settings.
The one dimension that needs a second word, because a caller’s rule
differs: a lambda given to a method on a Par[T] runs on several cores at
once and must be sync (Part II 12.6), where one given to a Seq[T] may
pause. Otherwise Par[T] has Seq[T]’s surface, and at
user_parallelism = no it walks in order on one thread, which Part II 10.7
already promises of a par_fold.
A program cannot write Seq[T] or Par[T]; it writes keys(), for,
collect(), and the ledger does the naming. A function of the program’s
own that hands back a produced sequence is a decision about laziness at the
surface of its own, and it is not taken here.
Changed by ADR-293 D16: three words after Seq[T] do distinguish these — ends (walkable from the back), sized (length known before the walk) and replays (a walk does not use it up); only the views stay the element type’s.
Whether the elements are views into a buffer is the element type’s
(Seq[ref String], with Part I 6.6’s rules); whether the order is stable is the
container’s (HashMap has none); whether the length is known is D1’s split.
No word for any of the three.
The chain keys().collect() is a Seq then a Vec, and names.sort() finds
its method. throws on a Seq is the
one spelling of a step can fail, which is what a loop over io::lines()
already costs the function.
What a reader gains is a signature that says what map does to what it
is given — [T, U](self: Seq[T], f: fn(T) -> U) -> Seq[U] — where it said ?.
What it costs: the entries of every std function that produces or
consumes a sequence, rewritten; a receiver rule in the checker for the two
words; and the once-only refusal, which is keeps asked of a Seq.
Seq that is also indexable, a range with a length. It is a
container here and walked as a Seq; a program that wants both at once
on one value has not been written.