Iterable
is Matchable
but IterableOnce
is not. Is there a fundamental reason for that?
Matchable is an opt-in marker that says “decomposing values of this type with pattern matching cannot break the abstraction in an unexpected way.” If a trait does not extend Matchable
the compiler merely emits a warning when you try to match on it, but library authors can decide case-by-case whether that extra promise is safe for their types.
IterableOnce
has onlyAny
as a super-type – it is not declaredMatchable
(IterableOnce)- Concrete sub-types may add the promise themselves; for example
Iterator
andIterable
both inheritMatchable
directly in the library definition (Iterator)
Why the library stops at IterableOnce
.
-
IterableOnce
is deliberately minimal: the only thing it guarantees is that you can obtain one iterator. A match expression such asxs match case Seq(a, b) => … // uses Seq.unapplySeq
will traverse the value to discover its shape. If
xs
is really anIterator
, that traversal consumes the iterator, changing its subsequent observable behaviour. Requiring the user (or the subtype) to say “yes, this is match-safe” forces them to think about that side effect. -
Some one-shot data sources (database cursors, input streams, generators, etc.) implement
IterableOnce
precisely because they do not want a second traversal. Marking the root trait asMatchable
would silently make those types decomposable, opening the door to subtle bugs. -
The extra promise matters for type-soundness, too.
Matchable
is used by the compiler to forbid pattern matches that could pierce opaque or abstract type boundaries (The Matchable Trait). Keeping the root trait outside that promise leaves room for user-defined single-pass collections whose internals must stay hidden.
Why Iterable
and Iterator
still extend it.
Iterable
is multi-pass by contract, so matching is harmless.Iterator
extendsMatchable
for backward compatibility: a great deal of existing code relies on matching over iterators, and the library authors judged that the convenience outweighs the risk. (You still need to remember that the iterator will be advanced by the extractor.)
So there is no deep type-theoretic limitation—just a conservative library design: the most generic collection trait stays neutral, and each concrete subtype opts in only if its semantics make pattern matching a safe operation.