-
Notifications
You must be signed in to change notification settings - Fork 1.1k
A rule to make typing nested classes more flexible #14390
-
#14387 can replace some of #13657 but not all. For instance, the following would still require #13657 to be able to typecheck tail
:
extension [A](xs: {*} LazyList[A]) def map[B](f: A => B): {xs, f} LazyList[B] = final class Mapped extends LazyList[B]: this: {xs, f} Mapped => def isEmpty = false def head: B = f(xs.head) def tail: {this} LazyList[B] = xs.tail.map(f) // OK
But we can hopefully get on safer ground by adding an alternative rule. The idea is that the local class Mapped
sees the outer references f
and xs
through this
. You could imagine a meaning preserving transformation where f
and xs
are cached in additional fields of class Mapped
like this:
val this_f = f val this_xs = xs
and every reference to f
or xs
is replaced by this.this_f
and this.this_xs
.
Let's now compare the expression xs.tail.map(f)
of type {f, xs} LazyList[B]
with the expected type {this} LazyList[B]
. Since there are no tracked parameters enclosing the expression inside class Mapped
, every occurrence of f
or xs
in the capture set C
must have come through this
, so we can equivalently use this.this_f
and this.this_xs
, which are both subsumed by this
. In other words, to compare actual {f, xs}
with expected {this}
, we can ignore any reference in the actual capture set that goes to a variable defined outside Mapped
, so both f
and xs
are discarded and the check succeeds.
More generally: When checking an expression e: Ca Ta
against an expected type Cx Tx
where the capture set of Cx
contains this
and any method inside the class Cls
of this
that contains e
has only pure parameters, drop from Ca
all references to variables or this
references outside Cls
. These are all accessed through this
, so are already accounted for by Cx
.
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 1 comment
-
I meditated a bit on this example and now I'm no longer sure why does it matter that Mapped
cannot "reach" xs
and f
otherwise than through its own members. @odersky, do you have an example that breaks if we don't have this restriction?
EDIT: ok, let me be a bit more explicit. If I look at the example, I already see xs
and f
inside Mapped
as references to fields of Mapped
- after all, that's also how this code is compiled, right? So if I get a value from outside that has the (surface) capture set of xs
, I understand that as a value that captures at most {this.this_xs}
, which is still strictly less things than {this}
, which must capture both of {xs,f}
({this.this_xs,this.this_f}
).
Beta Was this translation helpful? Give feedback.