-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extension methods #1122
Extension methods #1122
Conversation
Note, the terminology used in this proposal assumes #1119 is applied. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH I'm hesitant on this proposal... I get the impression it builds on qualified function calls, but to me "extension methods" means a significantly different thing in other languages. What's proposed here seems to miss the ergonomic benefits of the feature elsewhere.
It may make sense to provide, but I'd tend to wonder if the divergence from C++ is justified, given call syntax isn't on par with the equivalent in other languages.
- Improves ergonomics by allowing local, ad-hoc introduction of extension | ||
methods without the syntactic overhead of an adaptor. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unconvinced by the ergonomic argument.
This is noted by the example at https://en.wikipedia.org/wiki/Extension_method#Current_C#_solutions, where they compare string y = Utility.Reverse(x);
with the more desirable string y = x.Reverse();
. It seems like this proposal would provide string y = x.(Utility.Reverse)();
, but the key advantage noted in the article is eliding the Utility
container class (and also, unless qualified function calls turn out to be common, I think the extra parens will be awkward in practice).
Put differently, I feel like the main advantage of extension methods in other languages would be that they're seamless. However, the whole argument against that here is that you want to avoid new name lookup rules. In which case, I don't think extension methods are a reasonable comparison (and you might also want to consider the alternative: provide extension methods similar to how they exist elsewhere).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An ergonomic disadvantage is also that, assuming I'm understanding correctly that this only moves the location of Utility.Reverse
in the call, I think there'll be limited adoption. i.e., it's not so unambiguous an advantage that developers will flock to it (I don't understand why I would use this, but I assume other developers would think the opposite), and there'll just be multiple ways of offering member-method-like functions for classes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can get pretty close to the C# syntax, but it requires an explicit opt-in:
alias Reverse = Utility.Reverse;
// ...
var y: String = x.(Reverse)();
And yeah, I also don't expect developers to flock to this. But we need some rule in this space, and the only question seems to be how much of a barrier and what amount of language complication we want to put in front of people -- even the most restrictive of the alternatives below still permits writing extension methods, with more cumbersome syntax. The simple, orthogonal thing to do is to not restrict which functions can have a me
.
There is one way in which this isn't just moving the location of Utility.Reverse
in the call: only me
has the special addr me
behavior that implicitly takes the address of the value before the .
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One ergonomic advantage of method call syntax is that, if the codebase uses a natural language like English that has SVO word order, the method can sometimes be named so that the call syntax matches that order, which can make the code clearer. For example, I find x.Contains(y)
substantially clearer than Contains(x, y)
, because it matches the order of the English phrase "x contains y". That not only reduces my mental effort in parsing the code, it makes me more confident in the meaning of the code: x.Contains(y)
is very unlikely to mean "y contains x", whereas Contains(x, y)
plausibly could.
Extension methods make that option available even when the "subject" and "verb" are defined by different libraries. However, that may be undermined by the need to qualify the method name in most cases: x.(Utility.Contains)(y)
doesn't read very naturally, although it's probably still less ambiguous about the roles of x
and y
. And of course this advantage doesn't apply at all if the codebase's natural language doesn't use SVO order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be clear, I'm not objecting to extension methods (as a concept), I'm objecting that the syntactic overhead undermines the ergonomic benefit of this particular approach. Some of the question may be from the intuition: is compound member access going to be something that's really common in Carbon, or something that developers rarely see? My assumption is the latter, but maybe others differ.
If compound member access is common, then it reduces the cognitive costs of the feature by making it something people are accustomed to; if rare, then developers will be learning the feature when they see it (and perhaps forgetting it quickly due to disuse), and leading to slower understanding of code and likely more bugs.
Contains(x, y)
may not read as great, but it's going to be familiar to C++ developers as a function call, even versus x.(Contains)(y)
.
In other words -- maybe similar things would exist regardless, as noted in the alternatives, and determined developers would surely find them. But pushing developers towards a smaller set of common approaches has readability benefits too; I'd argue it's lower cognitive load for developers.
Co-authored-by: Jon Meow <[email protected]>
This seems a bit in conflict with the current plan for conditional methods. |
Can you say more about how these conflict? I think that conditional methods are a special case of the behavior proposed here, and if that's not the case then that is indeed a concern. |
The relevant parts of the discussion was:
(Assumption here is that The intent of conditional methods was: "
This is not necessarily a blocker for this proposal, but it was definitely a surprising consequence to me. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(discovered an old pending comment I forgot to post, sorry...)
**Note:** It's also possible to alias an extension method into a class and call | ||
it using simple member access notation. | ||
|
||
The type of `me` is not required to be a class type: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think i32
is going to be a class type though....
Maybe "There are no restrictions on the types that can be used with me
parameters."?
Explicitly say that we do not restrict which function declarations can have a
me
parameter, and that there is likewise no restriction on what type that parameter has. This enables a form of extension methods: