mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 13:50:10 +01:00
Updating self syntax and adding static member variables (#7016)
Update the syntax for class (and interface/`impl`) methods to move `self` into the parameter parentheses `()` and make the type in its binding optional (defaulting to `Self`). Introduce the `static` keyword for non-instance member variables to indicate static storage. Reflects the decision in leads issue [#6931](https://github.com/carbon-language/carbon-lang/issues/6931). Updates the directly relevant design, but leaves a systematic update of examples to a future PR. Assisted-by: Antigravity with Gemini --------- Co-authored-by: Geoff Romer <gromer@google.com> Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
co-authored by
Geoff Romer
Richard Smith
parent
71eed5b04a
commit
10b2b71847
+26
-25
@@ -64,7 +64,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
- [User-defined types](#user-defined-types)
|
||||
- [Classes](#classes)
|
||||
- [Assignment](#assignment)
|
||||
- [Class functions and factory functions](#class-functions-and-factory-functions)
|
||||
- [Non-instance member functions](#non-instance-member-functions)
|
||||
- [Methods](#methods)
|
||||
- [Inheritance](#inheritance)
|
||||
- [Access control](#access-control)
|
||||
@@ -972,7 +972,7 @@ _incomplete_, and in some cases there are limitations on what can be done with
|
||||
an incomplete name. Within a definition, the defined name is incomplete until
|
||||
the end of the definition is reached, but is complete in the bodies of member
|
||||
functions because they are
|
||||
[parsed as if they appeared after the definition](#class-functions-and-factory-functions).
|
||||
[parsed as if they appeared after the definition](classes.md#deferred-member-function-definitions).
|
||||
|
||||
A name is valid until the end of the innermost enclosing
|
||||
[_scope_](<https://en.wikipedia.org/wiki/Scope_(computer_science)>). There are a
|
||||
@@ -1655,12 +1655,12 @@ The order of the field declarations determines the fields' memory-layout order.
|
||||
|
||||
Classes may have other kinds of members beyond fields declared in its scope:
|
||||
|
||||
- [Class functions](#class-functions-and-factory-functions)
|
||||
- [Non-instance member functions](#non-instance-member-functions)
|
||||
- [Methods](#methods)
|
||||
- [`alias`](#aliases)
|
||||
- [`let`](#constant-let-declarations) to define class constants. **TODO:**
|
||||
Another syntax to define constants associated with the class like
|
||||
`class let` or `static let`?
|
||||
`static let`?
|
||||
- `class`, to define a
|
||||
[_member class_ or _nested class_](https://en.wikipedia.org/wiki/Inner_class)
|
||||
|
||||
@@ -1705,20 +1705,20 @@ sprocket = {.x = 2, .y = 1, .payload = "Bounce"};
|
||||
> - Proposal
|
||||
> [#981: Implicit conversions for aggregates](https://github.com/carbon-language/carbon-lang/pull/981)
|
||||
|
||||
#### Class functions and factory functions
|
||||
#### Non-instance member functions
|
||||
|
||||
Classes may also contain _class functions_. These are functions that are
|
||||
accessed as members of the type, like
|
||||
Classes may also contain _non-instance member functions_. These are functions
|
||||
that are accessed as members of the type, like
|
||||
[static member functions in C++](<https://en.wikipedia.org/wiki/Method_(computer_programming)#Static_methods>),
|
||||
as opposed to [methods](#methods) that are members of instances. They are
|
||||
commonly used to define a function that creates instances. Carbon does not have
|
||||
separate
|
||||
commonly used to define a function that creates instances (sometimes called
|
||||
factory functions). Carbon does not have separate
|
||||
[constructors](<https://en.wikipedia.org/wiki/Constructor_(object-oriented_programming)>)
|
||||
like C++ does.
|
||||
|
||||
```carbon
|
||||
class Point {
|
||||
// Class function that instantiates `Point`.
|
||||
// Non-instance member function that instantiates `Point`.
|
||||
// `Self` in class scope means the class currently being defined.
|
||||
fn Origin() -> Self {
|
||||
return {.x = 0, .y = 0};
|
||||
@@ -1762,20 +1762,20 @@ Class type definitions can include methods:
|
||||
```carbon
|
||||
class Point {
|
||||
// Method defined inline
|
||||
fn Distance[self: Self](x2: i32, y2: i32) -> f32 {
|
||||
fn Distance(self, x2: i32, y2: i32) -> f32 {
|
||||
var dx: i32 = x2 - self.x;
|
||||
var dy: i32 = y2 - self.y;
|
||||
return Math.Sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
// Mutating method declaration
|
||||
fn Offset[ref self: Self](dx: i32, dy: i32);
|
||||
fn Offset(ref self, dx: i32, dy: i32);
|
||||
|
||||
var x: i32;
|
||||
var y: i32;
|
||||
}
|
||||
|
||||
// Out-of-line definition of method declared inline
|
||||
fn Point.Offset[ref self: Self](dx: i32, dy: i32) {
|
||||
fn Point.Offset(ref self, dx: i32, dy: i32) {
|
||||
self.x += dx;
|
||||
self.y += dy;
|
||||
}
|
||||
@@ -1789,17 +1789,16 @@ Assert(origin.Distance(3, 4) == 0.0);
|
||||
This defines a `Point` class type with two integer data members `x` and `y` and
|
||||
two methods `Distance` and `Offset`:
|
||||
|
||||
- Methods are defined as class functions with a `self` parameter inside square
|
||||
brackets `[`...`]` before the regular explicit parameter list in parens
|
||||
`(`...`)`.
|
||||
- Methods are defined by declaring `self` as the first parameter in the
|
||||
parameter list in parens `(`...`)`.
|
||||
- Methods are called using the member syntax, `origin.Distance(`...`)` and
|
||||
`origin.Offset(`...`)`.
|
||||
- `Distance` computes and returns the distance to another point, without
|
||||
modifying the `Point`. This is signified using `[self: Self]` in the method
|
||||
modifying the `Point`. This is signified using `self` in the method
|
||||
declaration.
|
||||
- `origin.Offset(`...`)` _does_ modify the value of `origin`. This is
|
||||
signified using `[ref self: Self]` in the method declaration. It may only be
|
||||
called on [reference expressions](#expression-categories).
|
||||
signified using `ref self` in the method declaration. It may only be called
|
||||
on [reference expressions](#expression-categories).
|
||||
- Methods may be declared lexically inline like `Distance`, or lexically out
|
||||
of line like `Offset`.
|
||||
|
||||
@@ -1808,6 +1807,8 @@ two methods `Distance` and `Offset`:
|
||||
> - [Methods](classes.md#methods)
|
||||
> - Proposal
|
||||
> [#722: Nominal classes and methods](https://github.com/carbon-language/carbon-lang/pull/722)
|
||||
> - Proposal
|
||||
> [#7016: Updating `self` syntax and adding `static` fields](https://github.com/carbon-language/carbon-lang/pull/7016)
|
||||
|
||||
#### Inheritance
|
||||
|
||||
@@ -1954,13 +1955,13 @@ names resolvable by the compiler, and don't act like forward declarations.
|
||||
#### Destructors
|
||||
|
||||
A destructor for a class is custom code executed when the lifetime of a value of
|
||||
that type ends. They are defined with `fn destroy` followed by either
|
||||
`[self: Self]` or `[ref self: Self]` (as is done with [methods](#methods)) and
|
||||
the block of code in the class definition, as in:
|
||||
that type ends. They are defined with `fn destroy` followed by either `self` or
|
||||
`ref self` in the parameter list (as is done with [methods](#methods)) and the
|
||||
block of code in the class definition, as in:
|
||||
|
||||
```carbon
|
||||
class MyClass {
|
||||
fn destroy[self: Self]() { ... }
|
||||
fn destroy(self) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1969,7 +1970,7 @@ or:
|
||||
```carbon
|
||||
class MyClass {
|
||||
// Can modify `self` in the body.
|
||||
fn destroy[ref self: Self]() { ... }
|
||||
fn destroy(ref self) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
@@ -2169,7 +2170,7 @@ names earlier in the source than they are declared. In executable scopes such as
|
||||
function bodies, names declared later are not found. In declarative scopes such
|
||||
as packages, classes, and interfaces, it is an error to refer to names declared
|
||||
later, except that inline class member function bodies are
|
||||
[parsed as if they appeared after the class](#class-functions-and-factory-functions).
|
||||
[parsed as if they appeared after the class](classes.md#deferred-member-function-definitions).
|
||||
|
||||
A name in Carbon is formed from a sequence of letters, numbers, and underscores,
|
||||
and starts with a letter. We intend to follow
|
||||
|
||||
+144
-70
@@ -30,13 +30,17 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
- [Assignment and initialization](#assignment-and-initialization)
|
||||
- [Operations performed field-wise](#operations-performed-field-wise)
|
||||
- [Nominal class types](#nominal-class-types)
|
||||
- [Fields](#fields)
|
||||
- [Member variables](#member-variables)
|
||||
- [Fields](#fields)
|
||||
- [Static member variables](#static-member-variables)
|
||||
- [Initializers](#initializers)
|
||||
- [Syntax](#syntax)
|
||||
- [Forward declaration](#forward-declaration)
|
||||
- [`Self`](#self)
|
||||
- [Construction](#construction)
|
||||
- [Assignment](#assignment)
|
||||
- [Member functions](#member-functions)
|
||||
- [Class functions](#class-functions)
|
||||
- [Non-methods](#non-methods)
|
||||
- [Methods](#methods)
|
||||
- [Deferred member function definitions](#deferred-member-function-definitions)
|
||||
- [Name lookup in classes](#name-lookup-in-classes)
|
||||
@@ -69,7 +73,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
- [Discussion](#discussion)
|
||||
- [Inheritance](#inheritance-1)
|
||||
- [C++ abstract base classes interoperating with object-safe interfaces](#c-abstract-base-classes-interoperating-with-object-safe-interfaces)
|
||||
- [Overloaded methods](#overloaded-methods)
|
||||
- [Overloaded member functions](#overloaded-member-functions)
|
||||
- [Interop with C++ inheritance](#interop-with-c-inheritance)
|
||||
- [Virtual base classes](#virtual-base-classes)
|
||||
- [Mixins](#mixins-1)
|
||||
@@ -695,31 +699,68 @@ Declarations within a class should generally have the same syntax as
|
||||
declarations that occur in other contexts. For example, member functions are
|
||||
introduced with `fn`.
|
||||
|
||||
### Fields
|
||||
### Member variables
|
||||
|
||||
Fields of a nominal class type are declared with `var`:
|
||||
_Member variables_ are any `var`s declared within a class context (or in the
|
||||
future, potentially an interface or `impl` context).
|
||||
|
||||
```
|
||||
#### Fields
|
||||
|
||||
Instance member variables, or _fields_, are declared with `var`. They are
|
||||
associated with instances of the class, and determine the data layout of those
|
||||
instances.
|
||||
|
||||
#### Static member variables
|
||||
|
||||
Non-instance member variables, or _static member variables_, are declared with
|
||||
`static var`. They are associated with the type itself rather than instances of
|
||||
the type, and have static storage duration.
|
||||
|
||||
**Future work:** We should have a specific design for _storage duration_ in
|
||||
Carbon, and that should clarify what _static storage duration_ means. The intent
|
||||
is that it matches the general definition of
|
||||
[static variables](https://en.wikipedia.org/wiki/Static_variable).
|
||||
|
||||
```carbon
|
||||
class TextLabel {
|
||||
var x: i32;
|
||||
var y: i32;
|
||||
|
||||
// Static member variable.
|
||||
static var count: i32;
|
||||
|
||||
var text: String = "default";
|
||||
}
|
||||
```
|
||||
|
||||
Notice that this is subtly different from the meaning of `var` in other
|
||||
contexts: it declares an
|
||||
[instance variable](https://en.wikipedia.org/wiki/Instance_variable), not just a
|
||||
variable in the class's scope.
|
||||
Static member variable declarations are always definitions, similar to the
|
||||
current rules for global variables.
|
||||
|
||||
> **Open question:** Is there a way to declare class variables (scoped to the
|
||||
> class, not an instance)?
|
||||
**Future work:** We should add support for forward declaring static member
|
||||
variables and defining them later, including in an `impl` file. But ideally we
|
||||
should do that together with analogous support for global variables and using a
|
||||
cohesive set of rules.
|
||||
|
||||
In a field declaration, an initializer (such as `= "default"` above) specifies
|
||||
the default value of the field, and will be ignored if another value is supplied
|
||||
for that field when constructing an instance of the class. Defaults must be
|
||||
constants whose value can be determined at compile time.
|
||||
#### Initializers
|
||||
|
||||
In both field and static member variable declarations, an initializer (such as
|
||||
`= "default"` above) specifies the default or initial value. For a field, it
|
||||
will be ignored if another value is supplied for that field when constructing an
|
||||
instance of the class, and the default must be a constant whose value can be
|
||||
determined at compile time. For a static member variable, the initializer has
|
||||
the same behavior as an initializer of a global variable.
|
||||
|
||||
In all cases, the initializer expression is deferred and processed as-if it
|
||||
appeared immediately after the end of the outermost enclosing class, similar to
|
||||
[member function definitions](#deferred-member-function-definitions).
|
||||
|
||||
**Open question:** For a generic class, we will need to decide the rules for
|
||||
whether a static member variable's initializer is evaluated and storage created
|
||||
for it. That could occur for any specific of the class we monomorphize? Or only
|
||||
specifics of the static member variable that are referenced? Answering this is
|
||||
left as future work.
|
||||
|
||||
#### Syntax
|
||||
|
||||
The pattern in a field declaration must be a run-time binding pattern, so the
|
||||
full syntax is:
|
||||
@@ -727,6 +768,13 @@ full syntax is:
|
||||
_field-declaration_ ::= `var` _identifier_ `:` _expression_ [ `=` _expression_
|
||||
] `;`
|
||||
|
||||
Static member variable declarations provide the more general
|
||||
[_variable pattern_ syntax](values.md#binding-patterns-and-local-variables-with-let-and-var)
|
||||
based on the [`var` pattern modifier](pattern_matching.md#var):
|
||||
|
||||
_static-member-variable-declaration_ ::= `static` `var` _pattern_ [
|
||||
`=` _expression_ ] `;`
|
||||
|
||||
### Forward declaration
|
||||
|
||||
To support circular references between class types, we allow
|
||||
@@ -854,64 +902,68 @@ tl = {.x = 5, .y = 6};
|
||||
|
||||
### Member functions
|
||||
|
||||
Member functions can either be class functions or methods. Class functions are
|
||||
members of the type, while methods can only be called on instances.
|
||||
We consider all functions declared within a class, interface, or `impl` to be
|
||||
_member functions_. They are declared using `fn` and in the same way as normal
|
||||
functions, but within a class body. Member functions can also be instance
|
||||
_methods_ when their first explicit parameter is `self` as
|
||||
[described below](#methods).
|
||||
|
||||
#### Class functions
|
||||
Member functions are members of the type, and may be accessed using dot `.`
|
||||
member access on the type. The behavior of member access on an instance depends
|
||||
on whether the function is a method as described below.
|
||||
|
||||
A class function is like a
|
||||
[C++ static member function](https://en.cppreference.com/w/cpp/language/static#Static_member_functions),
|
||||
and is declared like a function at file scope. The declaration can include a
|
||||
definition of the function body, or that definition can be provided out of line
|
||||
after the class definition is finished. A common use is for constructor
|
||||
functions.
|
||||
Member functions may be defined lexically inline, or may be forward declared
|
||||
within the class and defined after the class definition. The body of a lexically
|
||||
inline member function is
|
||||
[processed in a deferred manner](#deferred-member-function-definitions).
|
||||
|
||||
```
|
||||
#### Non-methods
|
||||
|
||||
Member functions that aren't methods don't use any special syntax. They don't
|
||||
take a `self` parameter and so are regular functions, and work similarly to
|
||||
[C++ static member functions](https://en.cppreference.com/w/cpp/language/static#Static_member_functions).
|
||||
A common use is for constructor functions (sometimes called factory functions).
|
||||
|
||||
```carbon
|
||||
class Point {
|
||||
fn Origin() -> Self {
|
||||
return {.x = 0, .y = 0};
|
||||
}
|
||||
fn CreateCentered() -> Self;
|
||||
|
||||
var x: i32;
|
||||
var y: i32;
|
||||
}
|
||||
|
||||
fn Point.CreateCentered() -> Self {
|
||||
return {.x = ScreenWidth() / 2, .y = ScreenHeight() / 2};
|
||||
// No `self` parameter, so it's a regular, non-instance function.
|
||||
fn Origin() -> Self {
|
||||
return {.x = 0, .y = 0};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Class functions are members of the type, and may be accessed as using dot `.`
|
||||
member access either the type or any instance.
|
||||
Member access naming these functions on an instance of a type behaves the same
|
||||
as member access on the type itself.
|
||||
|
||||
```
|
||||
var p1: Point = Point.Origin();
|
||||
var p2: Point = p1.CreateCentered();
|
||||
```carbon
|
||||
var a: Point = Point.CreateCentered();
|
||||
// OK, `a` is evaluated and its value is discarded.
|
||||
var b: Point = a.CreateCentered();
|
||||
```
|
||||
|
||||
#### Methods
|
||||
|
||||
[Method](<https://en.wikipedia.org/wiki/Method_(computer_programming)>)
|
||||
declarations are distinguished from [class function](#class-functions)
|
||||
declarations by having a `self` parameter in square brackets `[`...`]` before
|
||||
the explicit parameter list in parens `(`...`)`. There is no implicit member
|
||||
access in methods, so inside the method body members are accessed through the
|
||||
`self` parameter. Methods may be written lexically inline or after the class
|
||||
declaration.
|
||||
declarations are member functions distinguished by having a `self` parameter as
|
||||
the first parameter in the explicit parameter list using parens `(`...`)`. The
|
||||
type in the binding syntax for the `self` parameter, typically `: Self`, is
|
||||
optional and if omitted defaults to `Self`. There is no implicit member access
|
||||
in methods, so inside the method body members are accessed through the `self`
|
||||
parameter.
|
||||
|
||||
```carbon
|
||||
class Circle {
|
||||
fn Diameter[self: Self]() -> f32 {
|
||||
fn Diameter(self) -> f32 {
|
||||
return self.radius * 2;
|
||||
}
|
||||
fn Expand[ref self: Self](distance: f32);
|
||||
fn Expand(ref self, distance: f32);
|
||||
|
||||
var center: Point;
|
||||
var radius: f32;
|
||||
}
|
||||
|
||||
fn Circle.Expand[ref self: Self](distance: f32) {
|
||||
fn Circle.Expand(ref self, distance: f32) {
|
||||
self.radius += distance;
|
||||
}
|
||||
|
||||
@@ -924,20 +976,31 @@ Assert(Math.Abs(c.Diameter() - 4.0) < 0.001);
|
||||
- Methods are called using the dot `.` member syntax, `c.Diameter()` and
|
||||
`c.Expand(`...`)`.
|
||||
- `Diameter` computes and returns the diameter of the circle without modifying
|
||||
the `Circle` instance. This is signified using `[self: Self]` in the method
|
||||
the `Circle` instance. This is signified using `self` in the method
|
||||
declaration.
|
||||
- `c.Expand(`...`)` does modify the value of `c`. This is signified using
|
||||
`[ref self: Self]` in the method declaration.
|
||||
`ref self` in the method declaration.
|
||||
|
||||
The pattern '`ref self:` _type_' means "the argument must be a
|
||||
[reference expression](/docs/design/values.md#reference-expressions), and must
|
||||
match the pattern '`self:` _type_'".
|
||||
The pattern `ref self` means "the argument must be a
|
||||
[reference expression](/docs/design/values.md#reference-expressions)".
|
||||
|
||||
Because methods are modeled as functions with an explicit `self` parameter, they
|
||||
can also be called directly by way of the type name without using dot-notation:
|
||||
|
||||
```carbon
|
||||
var d: f32 = Circle.Diameter(c);
|
||||
```
|
||||
|
||||
If the method declaration also includes
|
||||
[deduced compile-time parameters](/docs/design/generics/overview.md#deduced-parameters),
|
||||
the `self` parameter must be in the same list in square brackets `[`...`]`. The
|
||||
`self` parameter may appear in any position in that list, as long as it appears
|
||||
after any names needed to describe its type.
|
||||
they appear in square brackets `[`...`]` as usual, while `self` remains the
|
||||
first parameter in the parens `(`...`)`:
|
||||
|
||||
```carbon
|
||||
class Wrapper(T:! type) {
|
||||
fn Print[U:! type](self, x: U);
|
||||
}
|
||||
```
|
||||
|
||||
#### Deferred member function definitions
|
||||
|
||||
@@ -1197,18 +1260,16 @@ A base class may define
|
||||
methods whose implementation may be overridden in a derived class.
|
||||
|
||||
Only methods defined in the scope of the class definition may be virtual, not
|
||||
any defined in
|
||||
[out-of-line interface `impl` declarations](/docs/design/generics/details.md#out-of-line-impl).
|
||||
Interface methods may be implemented using virtual methods when the
|
||||
[impl is inline](/docs/design/generics/details.md#inline-impl), and calls to
|
||||
those methods by way of the interface will do virtual dispatch just like a
|
||||
direct call to the method does.
|
||||
|
||||
[Class functions](#class-functions) may not be declared virtual. Neither may
|
||||
functions with [compile-time parameters](/docs/design/generics/overview.md),
|
||||
whether those are `template` or checked, explicit or deduced. Compile-time
|
||||
parameters on the enclosing scope are allowed, though, so generic classes may
|
||||
have virtual methods.
|
||||
Functions with [compile-time parameters](/docs/design/generics/overview.md) may
|
||||
not be virtual, whether those are `template` or checked, explicit or deduced.
|
||||
Compile-time parameters on the enclosing scope are allowed, though, so generic
|
||||
classes may have virtual methods.
|
||||
|
||||
##### Virtual modifier keywords
|
||||
|
||||
@@ -2004,12 +2065,12 @@ different type than `DynPtr(MyInterface)` since the receiver input to the
|
||||
function members of the vtable for the former does not match those in the
|
||||
witness table for the latter.
|
||||
|
||||
#### Overloaded methods
|
||||
#### Overloaded member functions
|
||||
|
||||
We allow a derived class to define a [class function](#class-functions) with the
|
||||
same name as a class function in the base class. For example, we expect it to be
|
||||
pretty common to have a constructor function named `Create` at all levels of the
|
||||
type hierarchy.
|
||||
We allow a derived class to define a member function with the same name as a
|
||||
member function in the base class when neither are methods. For example, we
|
||||
expect it to be common to have a constructor function named `Make` at all levels
|
||||
of the type hierarchy.
|
||||
|
||||
Beyond that, we may want some rules or restrictions about defining methods in a
|
||||
derived class with the same name as a base class method without overriding it.
|
||||
@@ -2304,6 +2365,18 @@ the type of `U.x`."
|
||||
|
||||
- [#6008: Replace `impl fn` with `override fn`](https://github.com/carbon-language/carbon-lang/pull/6008)
|
||||
|
||||
- [#7016: Updating `self` syntax and adding `static` fields](https://github.com/carbon-language/carbon-lang/pull/7016)
|
||||
|
||||
- [Don't put `self` in either parameter list](/proposals/p7016.md#dont-put-self-in-either-parameter-list)
|
||||
- [`self` syntax in the deduced parameter list `[]`](/proposals/p7016.md#self-syntax-in-the-deduced-parameter-list-)
|
||||
- [`class` modifier for non-instance member variables and functions](/proposals/p7016.md#class-modifier-for-non-instance-member-variables-and-functions)
|
||||
- [Alternative keywords for non-instance member variables](/proposals/p7016.md#alternative-keywords-for-non-instance-member-variables)
|
||||
- [`shared`](/proposals/p7016.md#shared)
|
||||
- [`global`](/proposals/p7016.md#global)
|
||||
- [`static` for non-instance member functions](/proposals/p7016.md#static-for-non-instance-member-functions)
|
||||
- [`static` for package- and namespace-scope variables](/proposals/p7016.md#static-for-package--and-namespace-scope-variables)
|
||||
- [Distinct `method` introducer](/proposals/p7016.md#distinct-method-introducer)
|
||||
|
||||
## References
|
||||
|
||||
- [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257)
|
||||
@@ -2317,3 +2390,4 @@ the type of `U.x`."
|
||||
- [#2287: Allow unqualified name lookup for class members](https://github.com/carbon-language/carbon-lang/pull/2287)
|
||||
- [#2760: Consistent `class` and `interface` syntax](https://github.com/carbon-language/carbon-lang/pull/2760)
|
||||
- [#5017: Destructor syntax](https://github.com/carbon-language/carbon-lang/pull/5017)
|
||||
- [#7016: Updating `self` syntax and adding `static` fields](https://github.com/carbon-language/carbon-lang/pull/7016)
|
||||
|
||||
@@ -165,8 +165,8 @@ Other designs build upon basic function syntax to add advanced features:
|
||||
|
||||
- [Generic functions](generics/overview.md#generic-functions) adds support for
|
||||
deduced parameters and compile-time parameters.
|
||||
- [Class member functions](classes.md#member-functions) adds support for
|
||||
methods and class functions.
|
||||
- [Member functions](classes.md#member-functions) adds support for methods and
|
||||
non-instance member functions.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
- [Use case: Accessing interface names](#use-case-accessing-interface-names)
|
||||
- [Future work: Adapter with stricter invariants](#future-work-adapter-with-stricter-invariants)
|
||||
- [Associated constants](#associated-constants)
|
||||
- [Associated class functions](#associated-class-functions)
|
||||
- [Associated functions](#associated-functions)
|
||||
- [Associated facets](#associated-facets)
|
||||
- [Parameterized interfaces](#parameterized-interfaces)
|
||||
- [Parameterized named constraints](#parameterized-named-constraints)
|
||||
@@ -2236,11 +2236,16 @@ fn ExtractPoint[PointT:! NSpacePoint](
|
||||
**Aside:** The use of `:!` here means these `let` declarations will only have
|
||||
compile-time and not runtime storage associated with them.
|
||||
|
||||
### Associated class functions
|
||||
### Associated functions
|
||||
|
||||
To be consistent with normal
|
||||
[class function](/docs/design/classes.md#class-functions) declaration syntax,
|
||||
associated class functions are written using a `fn` declaration:
|
||||
Associated constants can also be _functions_. These are called _associated
|
||||
functions_, and include functions that are
|
||||
[methods](/docs/design/classes.md#methods).
|
||||
|
||||
To be consistent with
|
||||
[class member function](/docs/design/classes.md#member-functions) declaration
|
||||
syntax, associated functions are written using a `fn` declaration within the
|
||||
`interface` definition:
|
||||
|
||||
```carbon
|
||||
interface DeserializeFromString {
|
||||
@@ -2268,10 +2273,6 @@ var y: MySerializableType = Deserialize(MySerializableType, "4");
|
||||
This is instead of declaring an associated constant using `let` with a function
|
||||
type.
|
||||
|
||||
Together associated methods and associated class functions are called
|
||||
_associated functions_, much like together methods and class functions are
|
||||
called [member functions](/docs/design/classes.md#member-functions).
|
||||
|
||||
> **TODO:** Document rules on where associated function implementations can be
|
||||
> declared, as adopted in
|
||||
> [p5168: Forward `impl` declaration of an incomplete interface](/proposals/p5168.md).
|
||||
|
||||
@@ -434,8 +434,8 @@ _associated constant_. If the type of the associated constant is a
|
||||
which corresponds to what is called an "associated type" in other languages
|
||||
([Swift](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/generics/#Associated-Types),
|
||||
[Rust](https://doc.rust-lang.org/reference/items/associated-items.html#associated-types)).
|
||||
Similarly, an interface can have _associated function_, _associated method_, or
|
||||
_associated class function_.
|
||||
Similarly, an interface can have an _associated function_ (either an instance
|
||||
method or a non-instance function).
|
||||
|
||||
Different types can satisfy an interface with different definitions for a given
|
||||
member. These definitions are _associated_ with what type is implementing the
|
||||
|
||||
@@ -312,9 +312,9 @@ interoperability will include functions, primitive types, and structs that only
|
||||
contain member variables.
|
||||
|
||||
Features where interoperability will rely on more advanced C++-specific
|
||||
features, such as templates, inheritance, and class functions, need not be
|
||||
supported for C. These would require a C-specific interoperability model that
|
||||
will not be included.
|
||||
features, such as templates, inheritance, and non-instance member functions,
|
||||
need not be supported for C. These would require a C-specific interoperability
|
||||
model that will not be included.
|
||||
|
||||
## Non-goals
|
||||
|
||||
|
||||
@@ -131,7 +131,8 @@ would, in the cases where they overlap.
|
||||
|
||||
A name binding pattern is a pattern.
|
||||
|
||||
- _binding-pattern_ ::= `ref`? (_identifier_ | `self`) `:` _expression_
|
||||
- _binding-pattern_ ::= `ref`? (_identifier_ `:` _expression_ | `self` (`:`
|
||||
_expression_)?)
|
||||
- _binding-pattern_ ::= `template`? _identifier_ `:!` _expression_
|
||||
- _pattern_ ::= _binding-pattern_
|
||||
|
||||
@@ -193,13 +194,15 @@ fn F() -> i32 {
|
||||
}
|
||||
```
|
||||
|
||||
When `self` is used instead of an identifier, the pattern must appear in the
|
||||
implicit parameter list of a method (as discussed [here](classes.md#methods)).
|
||||
During pattern matching in a method call, the parameter pattern containing
|
||||
`self` is matched with the object that the method was invoked on. In all other
|
||||
respects, the `self` pattern behaves just like an ordinary binding pattern,
|
||||
introducing a binding named `self` into scope, just as if `self` were an
|
||||
identifier rather than a keyword.
|
||||
When `self` is used instead of an identifier, the pattern must appear as the
|
||||
first parameter in the explicit parameter list of a method, optionally nested
|
||||
within a `var` pattern, as discussed [here](classes.md#methods). If the "`:`
|
||||
_expression_" is omitted, it defaults to `Self`. During pattern matching in a
|
||||
method call, the parameter pattern containing `self` is matched with the object
|
||||
that the method was invoked on, and the call arguments are matched against the
|
||||
subsequent parameters. In all other respects, the `self` pattern behaves just
|
||||
like an ordinary binding pattern, introducing a binding named `self` into scope,
|
||||
just as if `self` were an identifier rather than a keyword.
|
||||
|
||||
#### Anonymous bindings
|
||||
|
||||
|
||||
@@ -0,0 +1,390 @@
|
||||
# Updating `self` syntax and adding `static` member variables
|
||||
|
||||
<!--
|
||||
Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
Exceptions. See /LICENSE for license information.
|
||||
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
-->
|
||||
|
||||
[Pull request](https://github.com/carbon-language/carbon-lang/pull/7016)
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Abstract](#abstract)
|
||||
- [Problem](#problem)
|
||||
- [Background](#background)
|
||||
- [Proposal](#proposal)
|
||||
- [Details](#details)
|
||||
- [Member variables](#member-variables)
|
||||
- [Member functions](#member-functions)
|
||||
- [Calling without method syntax](#calling-without-method-syntax)
|
||||
- [Rationale](#rationale)
|
||||
- [Alternatives considered](#alternatives-considered)
|
||||
- [Don't put `self` in either parameter list](#dont-put-self-in-either-parameter-list)
|
||||
- [`self` syntax in the deduced parameter list `[]`](#self-syntax-in-the-deduced-parameter-list-)
|
||||
- [`class` modifier for non-instance member variables and functions](#class-modifier-for-non-instance-member-variables-and-functions)
|
||||
- [Alternative keywords for non-instance member variables](#alternative-keywords-for-non-instance-member-variables)
|
||||
- [`shared`](#shared)
|
||||
- [`global`](#global)
|
||||
- [`static` for non-instance member functions](#static-for-non-instance-member-functions)
|
||||
- [`static` for package- and namespace-scope variables](#static-for-package--and-namespace-scope-variables)
|
||||
- [Distinct `method` introducer](#distinct-method-introducer)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
## Abstract
|
||||
|
||||
Update the syntax for class (and interface/`impl`) methods to move `self` into
|
||||
the parameter parentheses `()` and make the type in its binding optional
|
||||
(defaulting to `Self`). Introduce the `static` keyword for non-instance member
|
||||
variables to indicate static storage. Reflects the decision in leads issue
|
||||
[#6931](https://github.com/carbon-language/carbon-lang/issues/6931).
|
||||
|
||||
## Problem
|
||||
|
||||
The placement of `self` in the parameter list has been a repeated source of
|
||||
debate around the syntax design of Carbon -- grouping it with deduced parameters
|
||||
like types is surprising for some readers and nearly unprecedented in modern
|
||||
programming languages. As this is an especially pervasive and impactful aspect
|
||||
of Carbon's syntax given the prevalence of methods, it is important to revisit
|
||||
the syntax here and make a durable decision on how to approach this part of
|
||||
Carbon's syntax.
|
||||
|
||||
There is also a problem that we don't have a syntax for representing
|
||||
_non_-instance data members. Especially as we look at more C++ interop and more
|
||||
migrations from Carbon to C++ it is important to have a clear and idiomatic
|
||||
syntax for these constructs.
|
||||
|
||||
Key questions this proposal should address include:
|
||||
|
||||
- Where to place `self` (in `[]` versus `()`).
|
||||
- How to distinguish instance methods from non-instance functions.
|
||||
- How to declare non-instance member variables without overloading keywords or
|
||||
creating confusion in interfaces vs classes.
|
||||
|
||||
## Background
|
||||
|
||||
Leads discussed various syntax options for `self` placement and non-instance
|
||||
members in issue
|
||||
[#6931](https://github.com/carbon-language/carbon-lang/issues/6931).
|
||||
|
||||
## Proposal
|
||||
|
||||
We propose the following updates to the syntax for class members:
|
||||
|
||||
1. **Move `self` to the `()` parameter list**: Instance methods declare `self`
|
||||
as the first parameter in the explicit parameter list. This aligns methods
|
||||
with a model where method calls are conceptually sugar for function calls
|
||||
with an explicit object argument.
|
||||
2. **Make `self` type optional**: Optionally allow omitting the type of `self`,
|
||||
which results in the type being exactly `Self`. Example:
|
||||
`fn MyMethod(self)`.
|
||||
3. **Use `static` for non-instance data members**: Use `static var` for data
|
||||
members that are part of the type rather than part of instances of the type.
|
||||
4. **No keyword for non-instance member functions**: Functions without a `self`
|
||||
parameter declared within a `class` are considered non-instance member
|
||||
functions without any extra syntax.
|
||||
|
||||
Treating methods as functions with an explicit `self` parameter means they can
|
||||
be called using standard function call syntax (for example,
|
||||
`MyClass.Method(obj)`), not just method call syntax (`obj.Method()`).
|
||||
|
||||
While methods can be invoked as regular functions, method syntax (`obj.Method`)
|
||||
continues to form a "bound member function" that adapts `obj`. This allows the
|
||||
call to be resolved appropriately even when `obj` requires conversion to match
|
||||
the type of `self`. Similarly, we expect `obj.(Class.Method)(args)` to follow
|
||||
the same model. Fundamentally, our goal is that this does not change the
|
||||
[instance binding model](/docs/design/expressions/member_access.md#instance-binding).
|
||||
|
||||
## Details
|
||||
|
||||
### Member variables
|
||||
|
||||
_Member variables_ are any `var`s declared within a class context (or in the
|
||||
future, potentially an interface or `impl` context).
|
||||
|
||||
Instance member variables, or _fields_, are unchanged in the current design.
|
||||
|
||||
This proposal adds support for non-instance member variables, or _static member
|
||||
variables_, using the `static` modifier keyword on the `var` declaration. Static
|
||||
member variables are associated with the type itself rather than instances of
|
||||
the type, and have static storage duration.
|
||||
|
||||
```carbon
|
||||
class Widget {
|
||||
// Non-instance member variable.
|
||||
static var count: i32;
|
||||
|
||||
// Non-instance member function.
|
||||
fn ResetCount() { count = 0; }
|
||||
}
|
||||
```
|
||||
|
||||
This does not apply to package- and namespace-scope variables (if we support
|
||||
them) because static storage duration is the default in those contexts. It is
|
||||
currently limited to classes, but if we allow variables with static storage
|
||||
duration in other contexts that don't have a strong default of that storage,
|
||||
like interfaces and `impl` declarations, they should use the same syntax.
|
||||
|
||||
### Member functions
|
||||
|
||||
We propose retiring the terminology "class function". It is confusing when used
|
||||
in contexts like an interface (which is not a class) or where "class" implies
|
||||
other semantics. Instead, we consider all functions declared within a class,
|
||||
interface, or `impl` to be _member functions_. Member functions are either
|
||||
_methods_ (taking a `self` parameter) or _non-instance member functions_.
|
||||
|
||||
In practice, we most often are referring to member functions broadly, regardless
|
||||
of whether they are methods or not, or referring very specifically to methods.
|
||||
Because referring to the broad set happens much more than referring narrowly to
|
||||
non-instance member functions, we prioritize terminology that builds on a simple
|
||||
broad term. We also don't expect this to compound with other adjectives like
|
||||
"associated" as those will already imply "member", and so would be _associated
|
||||
functions_ for the broad group and _associated methods_ for the common narrow
|
||||
group.
|
||||
|
||||
Non-instance member functions don't use any special syntax. They don't take a
|
||||
`self` parameter and so are regular functions. For example:
|
||||
|
||||
```carbon
|
||||
class Point {
|
||||
var x: i32;
|
||||
var y: i32;
|
||||
|
||||
// No `self` parameter, so it's a regular, non-instance function.
|
||||
fn Create() -> Self {
|
||||
return {.x = 0, .y = 0};
|
||||
}
|
||||
}
|
||||
|
||||
fn F(p: Point) {
|
||||
var p2: Point = Point.Create();
|
||||
}
|
||||
```
|
||||
|
||||
Instance member functions, or _methods_, declare `self` as the first parameter
|
||||
in the `()` list. Specifying the type of `self` is optional and defaults to
|
||||
`: Self`.
|
||||
|
||||
```carbon
|
||||
class Point {
|
||||
var x: i32;
|
||||
var y: i32;
|
||||
|
||||
// Explicit type in the `self` binding.
|
||||
fn GetX(self: Point) -> i32 { return self.x; }
|
||||
|
||||
// Omitted type in the `self` binding (defaults to Point).
|
||||
fn GetY(self) -> i32 { return self.y; }
|
||||
|
||||
// By-reference `self` binding.
|
||||
fn SetX(ref self, new_x: i32) { self.x = new_x; }
|
||||
}
|
||||
```
|
||||
|
||||
Moving `self` into the parentheses reinforces the conceptual model that method
|
||||
calls are sugar for function calls with an explicit object argument.
|
||||
Specifically, `obj.Method(args...)` is equivalent to
|
||||
`Type.Method(obj, args...)`.
|
||||
|
||||
This adopts a **partial-application model** which unifies methods and functions.
|
||||
A function is an "instance method" if and only if its first parameter is named
|
||||
`self`. It can always be called directly with the first argument passed to the
|
||||
`self` parameter. When `Method` names a function with a `self` parameter, the
|
||||
syntax `obj.Method` is just syntactic sugar for partially applying the `obj` to
|
||||
the `self` parameter, which can then be called as a function by passing
|
||||
arguments for the remaining parameters.
|
||||
|
||||
For non-instance member functions, the absence of a `self` parameter is
|
||||
sufficient to distinguish them from methods.
|
||||
|
||||
#### Calling without method syntax
|
||||
|
||||
Because methods are modeled as functions with an explicit `self` parameter, they
|
||||
can be called as ordinary functions using their qualified names, with the `self`
|
||||
argument as the first element of the explicit parameter list:
|
||||
|
||||
```carbon
|
||||
fn F(p: Point) {
|
||||
// Standard method call syntax:
|
||||
var x1: i32 = p.GetX();
|
||||
|
||||
// Called as an ordinary function:
|
||||
var x2: i32 = Point.GetX(p);
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
This proposal effectively advances Carbon's goals by focusing on:
|
||||
|
||||
- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write):
|
||||
Moving `self` to the explicit parameter list within parentheses aligns
|
||||
Carbon's syntax with essentially all modern programming languages that use
|
||||
an explicit object parameter, notably including C++23 itself, Python, and
|
||||
Rust. It also reduces the visual cost of non-generic methods by removing the
|
||||
need for an additional delimiter kind in their declaration. Making the
|
||||
`: Self` part optional further reduces clutter in the most common cases and
|
||||
matches the syntax used in other languages like Rust.
|
||||
- [Software and language evolution](/docs/project/goals.md#software-and-language-evolution):
|
||||
Focusing `static` on storage (member variables) provides a crisp definition
|
||||
that works unambiguously across classes, interfaces, and `impl` blocks.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
### Don't put `self` in either parameter list
|
||||
|
||||
We considered two approaches to modeling `self` that separated it from
|
||||
parameters of any kind, implicit or explicit.
|
||||
|
||||
1. We could place `self` in an independent position with a different set of
|
||||
delimiters such as:
|
||||
|
||||
```carbon
|
||||
class Point {
|
||||
var x: i32;
|
||||
var y: i32;
|
||||
|
||||
// `self` as a separate component, likely in between the implicit and
|
||||
// explicit parameter lists.
|
||||
fn Create[T:! type]<self>(x: T) -> Self {
|
||||
return {.x = x as i32, .y = 0};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. We could make `self` implicit, similar to C++.
|
||||
|
||||
We have generally been happy with `self` being explicit instead of implicit and
|
||||
so to an extent we didn't deeply consider (2) as that wasn't part of the problem
|
||||
we set out to solve. We're still comfortable with the rationale about this
|
||||
aspect of the syntax from [p0722](/proposals/p0722.md#full-receiver-type).
|
||||
|
||||
We did consider (1) but were unable to find a syntax that felt compelling.
|
||||
Taking a new balanced delimiter is an especially difficult and expensive choice
|
||||
for the syntax. And without that, most syntaxes felt verbose for something that
|
||||
we expect to be extremely common and a natural "default".
|
||||
|
||||
### `self` syntax in the deduced parameter list `[]`
|
||||
|
||||
We considered placing `self` in the deduced parameter list (for example,
|
||||
`fn F[self: Self](...)`), as this has been the historical syntax in Carbon.
|
||||
|
||||
However, users strongly associate the implicit parameter list in `[]`s with
|
||||
deduced, compile-time parameters, but the `self` parameter is a _runtime_
|
||||
argument passed explicitly by the caller. While we have syntactic distinctions
|
||||
such as the `!` and the `self` keyword, the contextual collision still adds some
|
||||
cognitive load for some readers. We do imagine potential runtime implicit
|
||||
parameters in the future, but these are expected to be rare enough to be easier
|
||||
to accommodate and to leverage distinguishing syntax.
|
||||
|
||||
In contrast to these challenges, putting `self` in the explicit parameter list
|
||||
`()` aligns with the semantics of `self` as being an otherwise-normal parameter.
|
||||
|
||||
Last but not least, most other languages with explicit object parameters put
|
||||
them at the start of the parameter list inside `()`s (see for example Python and
|
||||
Rust). Being consistent with how object parameters are modeled in other
|
||||
languages is expected to reduce confusion and surprise for readers of Carbon.
|
||||
This is especially nice here, as we're asking C++ programmers to move from
|
||||
_implicit_ object parameters to _explicit_ object parameters -- aligning with
|
||||
other languages' explicit object parameter syntax hopefully minimizes that cost.
|
||||
|
||||
There was some concern that removing `self` from the `[]`s would result in
|
||||
readers assuming that the `[]`s can _only_ contain type parameters, similar to
|
||||
how Python works. However, that concern didn't carry as much weight as the other
|
||||
considerations for the leads.
|
||||
|
||||
Ultimately, the leads preferred the placement in the `()`s and the associated
|
||||
tradeoffs with that syntax.
|
||||
|
||||
### `class` modifier for non-instance member variables and functions
|
||||
|
||||
We considered using `class var` and `class fn` to mark non-instance member
|
||||
variables and functions. However, `class` is already an introducer in Carbon.
|
||||
Using it as a modifier adds an overloaded meaning. This meaning would also
|
||||
diverge from usage in other languages. For example, Swift uses `class` versus
|
||||
`static` to distinguish between class and struct methods, and has complex
|
||||
virtual dispatch rules that aren't part of the Carbon design. These members also
|
||||
appear in interfaces and `impl` blocks where the `class` keyword would be mildly
|
||||
surprising.
|
||||
|
||||
### Alternative keywords for non-instance member variables
|
||||
|
||||
The decision to use `static` at all in Carbon is relatively controversial. It
|
||||
has a history in C++ of being used for a sprawling and seemingly ever-growing
|
||||
set of things, in addition to the already subtle meanings inherited from C.
|
||||
|
||||
We chose `static` for non-instance member variables because of widely Carbon's
|
||||
major peer languages (notably C++, Rust, Swift, and Java) use the keyword
|
||||
`static` to mark class member variables with static storage duration. Reusing
|
||||
this spelling avoids a surprising divergence from the languages our users are
|
||||
most likely to be familiar with, and provides a clear, specific meaning focused
|
||||
on storage duration. This aligns with common definitions of
|
||||
[static variables](https://en.wikipedia.org/wiki/Static_variable) focusing on
|
||||
lifetime contrasted with scope.
|
||||
|
||||
A key aspect of the leads being comfortable with this direction was having the
|
||||
storage implication of the keyword be fundamental to _any_ usage of it rather
|
||||
than diluting that meaning with uses in other contexts.
|
||||
|
||||
Given that the `static` keyword did come with all of these concerns, we
|
||||
considered a number of alternatives and the specific rationale for not selecting
|
||||
those is recorded here.
|
||||
|
||||
#### `shared`
|
||||
|
||||
We considered using `shared var` to mark non-instance member variables. However,
|
||||
this term is heavily associated with concurrency and thread safety (for example,
|
||||
shared ownership, shared pointers). Reserving it for class members would collide
|
||||
with its use for future concurrency features (see
|
||||
[concurrency control design](https://docs.google.com/document/d/1WVWcmJdVBlapza_kPj2l3mOO-yw_hNXpb2u-Ren-I5M/)
|
||||
and
|
||||
[shared memory in CUDA](https://developer.nvidia.com/blog/using-shared-memory-cuda-cc/)).
|
||||
|
||||
The risk of confusion outweighed the benefits of identifying member variables
|
||||
that are conceptually shared across instances.
|
||||
|
||||
#### `global`
|
||||
|
||||
We considered using `global var` to mark non-instance member variables. However,
|
||||
`global` is most widely used to refer to _scope_ (global scope), whereas these
|
||||
members are explicitly scoped to a type (or potentially an interface). The most
|
||||
common distinction is that of "global" implicating scope or access, while
|
||||
"static" more consistently describes the storage model.
|
||||
|
||||
### `static` for non-instance member functions
|
||||
|
||||
We considered applying `static` to both member variables and functions (for
|
||||
example, `static fn F()`), for consistency with C++, Java, C#, and Swift.
|
||||
However, functions do not have instances or storage in the same way data members
|
||||
do. The absence of a `self` parameter is sufficient to distinguish instance
|
||||
methods from non-instance functions, making the keyword redundant for functions.
|
||||
|
||||
Reusing `static` here would stretch or dilute its meaning in Carbon. Not using
|
||||
it lets us keep the meaning narrow, and very specific to storage. It also helps
|
||||
emphasize the underlying unification that we have for member functions but not
|
||||
for data members: methods _are_ member functions, and can even be called as such
|
||||
when a viable object is explicitly passed to the `self` parameter. The only
|
||||
thing special about methods is that they _additionally_ allow the method call
|
||||
syntax.
|
||||
|
||||
### `static` for package- and namespace-scope variables
|
||||
|
||||
We considered whether to require or allow the `static` keyword on package- and
|
||||
namespace-scope variables (if we support them).
|
||||
|
||||
The storage of these variables should be described as "static" the same way as
|
||||
it is for static member variables. However, we propose omitting the keyword in
|
||||
those contexts because static storage is the strong default. In contrast, the
|
||||
strong default for class members is instance storage, so a keyword is needed to
|
||||
opt out of that default.
|
||||
|
||||
### Distinct `method` introducer
|
||||
|
||||
We considered using a distinct introducer like `method F()` instead of
|
||||
`fn F(self)`. However, we prefer a uniform `fn` introducer for all functions,
|
||||
relying on the presence of the `self` parameter to identify methods. This
|
||||
provides greater consistency across all different kinds of functions, including
|
||||
lambdas. It also embraces the unification of methods and functions.
|
||||
Reference in New Issue
Block a user