Open vasslitvinov opened 3 years ago
I'm not sure I'm sold on Self
as the keyword for the implicit self-reference. My intuition goes to this
because within the context of an interface it seems pretty obvious it would refer to the type itself (similar to how this
within a method refers to the type or value to which the method is being applied). But I recall there being a slight subtlety or potential point of confusion to this choice in past discussions (but don't recall what it was). I'd probably pick self
over Self
if we stuck with that term. If we pick an identifier other than this
for this, should we reserve it as a keyword, similar to how this
is?
We have not, so far, chosen this
exactly because of the ambiguity with this
inside a method. Especially when an interface contains a default implementation for a required method, we want to be able to refer both to this
-the-receiver and this
-the-interface formal.
I agree, our identifier of choice should be reserved, similarly to this
.
Especially when an interface contains a default implementation for a required method, we want to be able to refer both to this-the-receiver and this-the-interface formal.
Can you give an example here illustrating this?
when an interface contains a default implementation for a required method, we want to be able to refer both to this-the-receiver and this-the-interface formal
Here is an example:
interface Ifc(This) {
proc doit(arg: This);
proc This.helper() { doit(this); }
}
Eww, yeah, that is a bit unfortunate. Is it also the case that we can/might want to refer to This
within the body of a default implementation? I.e., could one write:
interface Ifc(This) {
proc This.helper() { writeln(This:string); }
}
Right, we could potentially. As of now, we have not worked out how to deal with types and params within interface-constrained functions.
Even if we draw the line "function header ==> this
means the interface formal, function body ==> this
means the receiver" and make it work, that would be too confusing to my eye.
Even if we draw the line "function header ==> this means the interface formal, function body ==> this means the receiver" and make it work, that would be too confusing to my eye.
That's exactly why I was asking. Though I agree it's subtle, if we didn't think such patterns would be written very often, I would consider it to avoid having to introduce a new keyword for this single purpose.
Just as a starting sanity-check, when you wrote:
interface Ifc(This) {
proc doit(arg: This);
proc This.helper() { doit(this); }
}
did you mean this:
interface Ifc(type This) {
proc doit(arg: This);
proc This.helper() { doit(this); }
}
(maybe the type
is optional? I'm not wild about that...), and is it correct that that's equivalent to this today:
interface Ifc {
proc doit(arg: Self);
proc Self.helper() { doit(this); }
}
Follow-up question: Within the body of an interface's method on Self
/This
, am I correct that this.type == This/Self
? That is, if we didn't have separate identifiers for this
and This
/Self
would we still be able to refer to both things within the body of the function?
Brainstorming other ways to express the type of a single-type interface, what about:
interface T?.Ifc { // query the type to which the interface is applied rather than relying on an implicit type
proc doit(arg: T);
proc T.helper() { doit(this); }
}
I'm feeling more allergic to the notion of having this
in an interface function's argument list refer to the interface's type because it seems like we probably ought to permit formal argument lists in normal methods to refer to this
like they can refer to other preceding arguments. That is, if I can write proc foo(x, y: x.type) { ... }
, it seems like I should probably be able to write proc R.foo(x, this.eltType) { ... }
where this
refers to the R
value on which the method was called, essentially... And if I can do it on a method, I'd want to be able to do it on an interface function as well.
That said, I could imagine writing your example as:
interface Ifc {
proc doit(arg: this.type); // doit is not a method, so `this` refers to Ifc's type
proc this.helper() { doit(this); } // this is a method on type `this`, so the first `this` refers to Ifc's type; the second refers to the value, like it always does within a method
proc type this.helper() { doit(new this()); } // this is a type method on type `this`, so the first `this` refers to Ifc's type again; and so does the second, since it's a type method
}
Is this subtle and potentially confusing? Yes. Does it make consistent sense with other aspects of the language? I'd argue also yes. Is it a preferred form? Probably not, in which case we'd suggest the user rewrite it using an explicit type argument:
interface Ifc(type T) {
proc doit(arg: T);
proc T.helper() { doit(this); }
proc type T.helper() { doit(new T()); }
}
or the query format proposed above, if considered sufficiently attractive/clear:
interface T?.Ifc {
proc doit(arg: T);
proc T.helper() { doit(this); }
proc type T.helper() { doit(new T()); }
}
main issue: #8629
Which of the following styles do we want to support for an interface declaration?
(implicit) the type
Self
is implicitly the formal This would be particularly useful if we adopt (only-methods) from #16800 and/or disallow free functions, discussed in #16851(explicit) the formal(s) are listed explicitly
(with-intent) the formal(s) are listed explicitly with
type
orparam
intent(s)Related questions:
can an interface have multiple formals? is there a compelling use case for it?
can an interface have
param
formal(s)? is there a compelling use case for it?