Update to the syntax for class members: functions, methods, fields, etc.
leads question
We've had a bunch of discussions around different syntax tweaks for `class` members. I'm not going to try to summarize all of the iterations here, but I think the various discussions have started to converge and I want to file a leads issue to confirm "yes, this is the direction we like" or suggest alternatives.
Also, a couple of minor sub-questions still need to be resolved.
### Summary
- Make non-instance methods (aka static member functions in C++) and non-instance variables (aka static member variables in C++) use the `class` introducer keyword: `class var x: i32;`, or `class fn Make() -> Self;`.
- Move the `self: ...` declaration for the object parameter of instance methods (aka non-static member functions in C++) into the `(...)` parameter list as the first parameter.
- Allow omitting the `: ...` from `self` specifically to get a language-provided default of `Self`. This still allows `(ref self, ...)` to declare a method that takes the object as a `ref`-binding.
### Details
#### `class` introducer
Adding the `class` introducer solves two problems:
- We need a way to write what C++ uses "static data members" for, currently we only have fields.
- It makes instance methods and class functions have a better ergonomic balance rather than having a significant penalty for the common case of instance methods
We suggest `class` as the use of `static` in C++ has been a continual source of confusion. For example, these are not "static" in contrast to "dynamic" -- they are very often dynamic objects.
**Open question (1):** How do we want to handle class _constants_? We could write `class let ElementT:! type = ...;`, or we could say that with `!`, the `class` isn't needed because it cannot mean anything else. I lean towards the latter, but we should make a clear decision here.
#### `self` placement
We suggest moving the `self` into the parentheses. These are the parameters most similar to `self`, even though there are still real differences. Keeping `self` explicit continues to be something people appreciate as it makes the body of instance methods more clear when accessing instance fields vs. other data.
We also suggest switching "method call" to conceptually follow the model of partial-application of the object parameter to the function. As a consequence, _all_ of these are "just functions", but instance methods have a special ability to be called using method notation: `x.Foo()`. This may suggest a slight refinement of method binding design.
We suggest allowing explicit non-method syntax calls even to functions with a `self` parameter, mostly because it seems to follow naturally from the signature and model of partial application. While we don't really have a strong motivating use case for this, we would have to somewhat go out of our way to reject it and it seems harmless to have and maybe a mild convenience in some cases.
We suggest restricting the method syntax and partial application to functions with a first parameter of `self`, not any function. This restriction seems useful to ensure code is written consistently and unsurprisingly. We don't see significant downsides to the restriction.
Similarly, we suggest restricting `self` to be the _first_ parameter in the list.
**Open question (2):** At what point do we want to do the partial application? And/or, exactly how do we want to decompose a simple member function call into a compound member function call?
Note that there has been a _lot_ of discussion about the placement and the tradeoff between in the `()`s vs. the `[]`s. Ultimately, they both looked viable. @KateGregory and I discussed this at length, and ultimately we both somewhat preferred putting it in the `()`s, but mostly as a matter of aesthetics and taste. We also see further syntax evolution likely to reinforce this fit in minor ways.
#### `self` abbreviation
It seems natural to omit the `: Self` and provide that as a default. It can still be written explicitly when it is useful, for example when converting the object parameter to a different type. When written, the type is a normal type expression. When omitted, it isn't a _deduced_ type or something else, it is always exactly `: Self`.
#### C++ interop
We also need to model calls to C++ overload sets that mix instance methods and static member functions. My suggestion is to model an overload set as an instance method overload set (and allow method call syntax) if any overload in the set is an instance method. The object parameter can then be discarded if the selected overload is in fact a static member function. Only overload sets where all functions are static member functions can be called using class function syntax from Carbon.
---
Let me know if I missed any important aspects or open questions here! Especially Kate and Richard -- please update with any important points from our various discussions that I missed to make sure we have a record and hear any feedback on them.
23 条评论