Skip to content

Fuse mapWithIndex into a single lazy view, avoiding tuple allocation - #86

Open
cheeseng wants to merge 1 commit into
dotty-staging:stdlib/map-with-indexfrom
artimahub:stdlib/map-with-index-artima-cs
Open

Fuse mapWithIndex into a single lazy view, avoiding tuple allocation#86
cheeseng wants to merge 1 commit into
dotty-staging:stdlib/map-with-indexfrom
artimahub:stdlib/map-with-index-artima-cs

Conversation

@cheeseng

Copy link
Copy Markdown

mapWithIndex composed two existing view combinators:

new View.Map(new View.ZipWithIndex(this), (pair: (A, Int)) => f(pair._1, pair._2))

ZipWithIndex's iterator constructs a real (A, Int) tuple per element;
View.Map's function then immediately destructures that tuple and
discards it. This also means two View instances and, once traversed,
two chained Iterator instances wrap this, where one fused instance
would do.

This PR replaces that with a single anonymous AbstractView[B] that
threads the index as a mutable field on its own iterator rather than
pairing it with each element.

self is captured explicitly, since an unqualified this inside the
anonymous AbstractView body refers to the view itself rather than the
enclosing collection. The inner iterator method is annotated
Iterator[B]^{self, f} so the capture checker accepts that it holds
onto self and f, mirroring the outer method's own CC[B]^{this, f}
annotation.

Laziness is preserved: nothing in this is evaluated until the
resulting view is traversed, matching existing tests asserting zero
element evaluations immediately after calling mapWithIndex.

No behavior changes: same result values, same traversal order, same
laziness guarantees as before.

mapWithIndex was implemented as
new View.Map(new View.ZipWithIndex(this), (pair: (A, Int)) => f(pair._1, pair._2)),
which builds a real (A, Int) tuple per element via ZipWithIndex only
to have View.Map immediately unpack and discard it one step later. It
also allocates two View objects and, once traversed, two chained
Iterator objects instead of one.

Replace it with a single anonymous AbstractView[B] whose iterator
threads the index as a plain mutable field instead of pairing it with
each element.

`self` is captured explicitly since an unqualified `this` inside the
anonymous AbstractView body would refer to the view itself. The
inner iterator's return type is annotated Iterator[B]^{self, f} so
the capture checker accepts that it legitimately holds onto self and
f, matching how the outer method is already annotated CC[B]^{this, f}.

Preserves the original's laziness: no element of `this` is evaluated
until the resulting view is actually traversed.

No behavior changes; result values, traversal order, and laziness are
identical to the previous implementation.
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