Implementation inheritance for classes#209
Conversation
|
Pretty decent RFC, although I wonder how will the relationship between abstract classes and interfaces will be, if they're both implemented. I think the only difference would be that a class can implement multiple interfaces, while a class can only inherit from one abstract class. That aside, I want to ask the possibility of a |
| The original class RFC states that class names are hoisted and so can be used before the class declaration has been evaluated. This is unchanged by this RFC, but carries with it a consequence that is important to call out: | ||
|
|
||
| Class declarations have effects at the top level. Therefore, a class cannot inherit from another class that occurs lexically after it within the module. | ||
|
|
||
| ``` | ||
| class Child extends Base -- error: Base is nil here! | ||
| end | ||
|
|
||
| class Base | ||
| end | ||
|
|
||
| class SecondChild extends Base -- OK | ||
| end | ||
| ``` | ||
|
|
||
| Developers are advised to order their class declarations accordingly. We will likely need a lint rule to detect this case. |
There was a problem hiding this comment.
Again, a good example of why hoisting is a terrible idea. This is a textbook TDZ in JavaScript. The small ergonomic gain hoisting brings is squashed by unintuitive restrictions like these. It would be both simpler and more robust to just not hoist class identifiers at all.
There was a problem hiding this comment.
I heavily agree. It just feels like a weird workaround for a promised optimization opportunity.
| Adding implementation inheritance reduces optimization opportunities involving class method inlining and dispatch. For example, consider the following: | ||
|
|
||
| ``` | ||
| function callToString(a: Animal) | ||
| return a.__tostring() | ||
| end | ||
| ``` | ||
|
|
||
| Since `Animal` has been subclassed by `Cat`, we can no longer statically determine which method to dispatch within the body of `callToString`. To address this, we may need a `final` keyword preventing classes from being subclassed. |
There was a problem hiding this comment.
This is a huge drawback that renders a bunch of decisions made in the previous RFC pointless (e.g. classes only allowed at top-level, no __index metamethods, no constructor protocol). If the plan was to drop those performance constraints all along, they shouldn't have been tailoring the discussion to begin with.
I was under the impression that the team was interested in a system similar to Rust's traits/interfaces that would be more optimization friendly. Why was this scrapped?
I'm not convinced redeclaring a field with an identical type should be forbidden. While allowing either subtypes or supertypes would be unsound for mutable fields, an identical redeclaration is probably harmless and may improve readability when documenting a subclass's complete public surface. That said, I'm not sure the additional complexity is justified so up in the air about this one. As a side note, I think some of the wording could be improved a little. This RFC talks about "methods" but then does things like The example provided in one of the drawbacks confuses me a little: function callToString(a: Animal)
return a.__tostring()
endThis might be totally valid code--my memory of the exact class semantics is admittedly not 100%--but metamethods are usually not directly accesible on a table (or instance in this case)?
This feels like a huge problem. I agree it's not this RFC's problem to solve, and I love the ideas that this RFC proposes, but this feels like a big problem that needs solved sooner rather than later. The more work that goes into classes that revolves around "if an argument comes first, and is named
I understand this, and do think it makes absolute sense (and somewhat avoids cyclic classes [though I worry if the cyclic requires may allow cyclic inheritance chains?]), but with how magic hoisting is I think this more draws attention to the problems presented by hoisting. That's not a strike against this RFC from me or even against this particular semantic, but more a general concern of where we're heading. |
|
In this RFC, it's stated that:
In the original classes RFC, it's stated that:
Does inheritance also inherit "metamethods"? Are they even metamethods, or just share names with metamethods? Both this and #210 call metamethod-shaped methods in ways that would normally not be possible for metamethods, so I'm not even sure if they're "methods" or "metamethods". (Akin perhaps to If inheritance does inherit metamethods, which I think would be nice, do we allow |
Lua 5.1, and therefore I assume also Luau, already defines how Also I agree it would be nice for metamethods to be inherited, and also overridable following the same logic as any other class method. I also generally agree that this is a good RFC but the comments highlight reason why now might not be the best time to move forward with this, and instead we need to resolve issues with self typing, hoisting, and the loss of optimisation potentinal promissed by classes. Only being able to extend abstract classes sounds like a great proposal for maintaining general optimisation potentinal. |
Rendered