diff --git a/.codespell_ignore b/.codespell_ignore index ab2f6f29a94c..9e2891769400 100644 --- a/.codespell_ignore +++ b/.codespell_ignore @@ -4,6 +4,7 @@ circularly copyable +crate inout pullrequest statics diff --git a/docs/design/README.md b/docs/design/README.md index 78b9799aa6d1..27b85695cd47 100644 --- a/docs/design/README.md +++ b/docs/design/README.md @@ -43,8 +43,11 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Arrays and slices](#arrays-and-slices) - [User-defined types](#user-defined-types) - [Classes](#classes) + - [Assignment, copying](#assignment-copying) + - [Member access](#member-access) + - [Methods](#methods) - [Allocation, construction, and destruction](#allocation-construction-and-destruction) - - [Assignment, copying, and moving](#assignment-copying-and-moving) + - [Moving](#moving) - [Comparison](#comparison) - [Implicit and explicit conversion](#implicit-and-explicit-conversion) - [Inline type composition](#inline-type-composition) @@ -599,8 +602,7 @@ fn RemoveLast(x: (Int, Int, Int)) -> (Int, Int) { > References: [Classes](classes.md) -Classes are a way for users to define their own data strutures or named product -types. +Classes are a way for users to define their own data strutures or record types. For example: @@ -620,11 +622,7 @@ Breaking apart `Widget`: - `Widget` has one `String` member: `payload`. - Given an instance `dial`, a member can be referenced with `dial.paylod`. -##### Allocation, construction, and destruction - -> **TODO:** Needs a feature design and a high level summary provided inline. - -##### Assignment, copying, and moving +##### Assignment, copying You may use a _structural data class literal_, also known as a _struct literal_, to assign or initialize a variable with a class type. @@ -641,6 +639,67 @@ var thingy: Widget = sprocket; sprocket = thingy; ``` +##### Member access + +The data members of a variable with a class type may be accessed using dot `.` +notation: + +```carbon +Assert(sprocket.x == thingy.x); +``` + +##### Methods + +Class type definitions can include methods: + +```carbon +class Point { + fn Distance[me: Self](x2: i32, y2: i32) -> f32 { + var dx: i32 = x2 - me.x; + var dy: i32 = y2 - me.y; + return Math.Sqrt(dx * dx - dy * dy); + } + fn Offset[addr me: Self*](dx: i32, dy: i32); + + var x: i32; + var y: i32; +} + +fn Point.Offset[addr me: Self*](dx: i32, dy: i32) { + me->x += dx; + me->y += dy; +} + +var origin: Point = {.x = 0, .y = 0}; +Assert(Math.Abs(origin.Distance(3, 4) - 5.0) < 0.001); +origin.Offset(3, 4); +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 functions with a `me` parameter inside square + brackets `[`...`]` before the regular explicit parameter list in parens + `(`...`)`. +- Methods are called using 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 `[me: Self]` in the method + declaration. +- `origin.Offset(`...`)` does modify the value of `origin`. This is signified + using `[addr me: Self*]` in the method declaration. +- Methods may be declared lexically inline like `Distance`, or lexically out + of line like `Offset`. + +##### Allocation, construction, and destruction + +> **TODO:** Needs a feature design and a high level summary provided inline. + +##### Moving + +> **TODO:** Needs a feature design and a high level summary provided inline. + ##### Comparison > **TODO:** Needs a feature design and a high level summary provided inline. diff --git a/docs/design/classes.md b/docs/design/classes.md index ff915aa0a9e4..09b33799785a 100644 --- a/docs/design/classes.md +++ b/docs/design/classes.md @@ -30,18 +30,26 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Type expression](#type-expression) - [Assignment and initialization](#assignment-and-initialization) - [Operations performed field-wise](#operations-performed-field-wise) -- [Future work](#future-work) - - [Nominal class types](#nominal-class-types) - - [Construction](#construction) - - [Member type](#member-type) +- [Nominal class types](#nominal-class-types) + - [Forward declaration](#forward-declaration) - [Self](#self) + - [Construction](#construction) + - [Assignment](#assignment) + - [Member functions](#member-functions) + - [Class functions](#class-functions) + - [Methods](#methods) + - [Name lookup in member function definitions](#name-lookup-in-member-function-definitions) + - [Nominal data classes](#nominal-data-classes) + - [Member type](#member-type) - [Let](#let) - - [Methods](#methods) + - [Alias](#alias) + - [Private access](#private-access) +- [Future work](#future-work) + - [Struct literal shortcut](#struct-literal-shortcut) - [Optional named parameters](#optional-named-parameters) - [Field defaults for struct types](#field-defaults-for-struct-types) - [Destructuring in pattern matching](#destructuring-in-pattern-matching) - [Discussion](#discussion) - - [Access control](#access-control) - [Operator overloading](#operator-overloading) - [Inheritance](#inheritance) - [C++ abstract base classes interoperating with object-safe interfaces](#c-abstract-base-classes-interoperating-with-object-safe-interfaces) @@ -75,8 +83,9 @@ class variables. The use cases for classes include both cases motivated by C++ interop, and cases that we expect to be included in idiomatic Carbon-only code. -**This design currently only attempts to address the "data classes" use case.** -Addressing the other use cases is future work. +**This design currently only attempts to address the "data classes" and +"encapsulated types without inheritance" use cases.** Addressing the other use +cases is future work. ### Data classes @@ -130,8 +139,7 @@ We expect two kinds of methods on these types: public methods defining the API for accessing and manipulating values of the type, and private helper methods used as an implementation detail of the public methods. -These types are expected in idiomatic Carbon-only code. Extending this design to -support these types is future work. +These types are expected in idiomatic Carbon-only code. #### With inheritance and subtyping @@ -509,8 +517,7 @@ but we expect later to support more ways to define data class types. Also note that there is no `struct` keyword, "struct" is just convenient shorthand terminology for a structural data class. -**Future work:** We intend to support nominal [data classes](#data-classes) as -well. +[Nominal data classes](#nominal-data-classes) are also supported by Carbon. ### Literals @@ -635,68 +642,67 @@ Destruction is performed field-wise in reverse order. Extending user-defined operations on the fields to an operation on an entire data class is [future work](#interfaces-implemented-for-data-classes). -## Future work +## Nominal class types -This includes features that need to be designed, questions to answer, and a -description of the provisional syntax in use until these decisions have been -made. +The declarations for nominal class types will have: -### Nominal class types +- `class` introducer +- the name of the class +- in the future, we will have optional modifiers for inheritance +- `{`, an open curly brace +- a sequence of declarations +- `}`, a close curly brace -The declarations for nominal class types will have a different format. -Provisionally we have been using something like this: +Declarations should generally match declarations that can be declared in other +contexts, for example variable declarations with `var` will define +[instance variables](https://en.wikipedia.org/wiki/Instance_variable): ``` class TextLabel { - var x: Int; - var y: Int; + var x: i32; + var y: i32; - var text: String; + var text: String = "default"; } ``` -It is an open question, though, how we will address the -[different use cases](#use-cases). For example, we might mark -[data classes](#data-classes) with an `impl as Data {}` line. +The main difference here is that `"default"` is a default instead of an +initializer, and will be ignored if another value is supplied for that field +when constructing a value. Defaults must be constants whose value can be +determined at compile time. -### Construction +### Forward declaration -There are a variety of options for constructing class values, we might choose to -support, including initializing from struct values: +To support circular references between class types, we allow +[forward declaration](https://en.wikipedia.org/wiki/Forward_declaration) of +types. A type that is forward declared is considered incomplete until the end of +a definition with the same name. ``` -var p1: Point2D = {.x = 1, .y = 2}; -var p2: auto = {.x = 1, .y = 2} as Point2D; -var p3: auto = Point2D{.x = 1, .y = 2}; -var p4: auto = Point2D(1, 2); -``` +class GraphNode; -### Member type +class GraphEdge { + var head: GraphNode*; + var tail: GraphNode*; +} -Additional types may be defined in the scope of a class definition. - -``` -class StringCounts { - class Node { - var key: String; - var count: Int; - } - var counts: Vector(Node); +class GraphNode { + var edges: Vector(GraphEdge*); } ``` -The inner type is a member of the type, and is given the name -`StringCounts.Node`. +**Open question:** What is specifically allowed and forbidden with an incomplete +type has not yet been decided. ### Self A `class` definition may provisionally include references to its own name in -limited ways, similar to an incomplete type. What is allowed and forbidden is an -open question. +limited ways. These limitations arise from the type not being complete until the +end of its definition is reached. ``` class IntListNode { - var data: Int; + var data: i32; var next: IntListNode*; } ``` @@ -706,7 +712,7 @@ current type, is: ``` class IntListNode { - var data: Int; + var data: i32; var next: Self*; } ``` @@ -716,48 +722,388 @@ class IntListNode { ``` class IntList { class IntListNode { - var data: Int; + var data: i32; var next: Self*; } var first: IntListNode*; } ``` -### Let +### Construction -Other type constants can provisionally be defined using a `let` declaration: +Any function with access to all the data fields of a class can construct one by +converting a [struct value](#struct-types) to the class type: ``` -class MyClass { - let Pi: Float32 = 3.141592653589793; - let IndexType: Type = Int; +var tl1: TextLabel = {.x = 1, .y = 2}; +var tl2: auto = {.x = 1, .y = 2} as TextLabel; + +Assert(tl1.x == tl2.x); + +fn ReturnsATextLabel() -> TextLabel { + return {.x = 1, .y = 2}; +} +var tl3: TextLabel = ReturnsATextLabel(); + +fn AcceptsATextLabel(tl: TextLabel) -> i32 { + return tl.x + tl.y; +} +Assert(AcceptsATextLabel({.x = 2, .y = 4}) == 6); +``` + +Note that a nominal class, unlike a [struct type](#type-expression), can define +default values for fields, and so may be initialized with a +[struct value](#literals) that omits some or all of those fields. + +#### Assignment + +Assignment to a struct value is also allowed in a function with access to all +the data fields of a class. Assignment always overwrites all of the field +members. + +``` +var tl: TextLabel = {.x = 1, .y = 2}; +Assert(tl.text == "default"); + +// ✅ Allowed: assigns all fields +tl = {.x = 3, .y = 4, .text = "new"}; + +// ✅ Allowed: This statement is evaluated in two steps: +// 1. {.x = 5, .y = 6} is converted into a new TextLabel value, +// using default for field `text`. +// 2. tl is assigned to a TextLabel, which has values for all +// fields. +tl = {.x = 5, .y = 6}; +Assert(tl.text == "default"); +``` + +**Open question:** This behavior might be surprising because there is an +ambiguity about whether to use the default value or the previous value for a +field. We could require all fields to be specified when assigning, and only use +field defaults when initializing a new value. + +``` +// ❌ Forbidden: should tl.text == "default" or "new"? +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. + +#### Class functions + +A class function is like a +[C++ static member function or method](), +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. + +``` +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}; } ``` -There are definite questions about this syntax: +Class functions are members of the type, and may be accessed as using dot `.` +member access either the type or any instance. -- Should these use the `:!` generic syntax decided in - [issue #565](https://github.com/carbon-language/carbon-lang/issues/565)? -- Would we also have `alias` declarations? They would only be used for names, - not other constant values. +``` +var p1: Point = Point.Origin(); +var p2: Point = p1.CreateCentered(); +``` -### Methods +#### Methods -A future proposal will incorporate -[method]() -declaration, definition, and calling into classes. The syntax for declaring -methods has been decided in -[question-for-leads issue #494](https://github.com/carbon-language/carbon-lang/issues/494). -Summarizing that issue: +[Method]() +declarations are distinguished from [class function](#class-functions) +declarations by having a `me` 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 `me` +parameter. Methods may be written lexically inline or after the class +declaration. -- Accessors are written: `fn Diameter[me: Self]() -> Float { ... }` -- Mutators are written: `fn Expand[addr me: Self*](distance: Float) { ... }` -- Associated functions that don't take a receiver at all, like - [C++'s static methods](), - are written: `fn Create() -> Self { ... }` +```carbon +class Circle { + fn Diameter[me: Self]() -> f32 { + return me.radius * 2; + } + fn Expand[addr me: Self*](distance: f32); -We do not expect to have implicit member access in methods, so inside the method -body members will be accessed through the `me` parameter. + var center: Point; + var radius: f32; +} + +fn Circle.Expand[addr me: Self*](distance: f32) { + me->radius += distance; +} + +var c: Circle = {.center = Point.Origin(), .radius = 1.5 }; +Assert(Math.Abs(c.Diameter() - 3.0) < 0.001); +c.Expand(0.5); +Assert(Math.Abs(c.Diameter() - 4.0) < 0.001); +``` + +- Methods are called using 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 `[me: Self]` in the method + declaration. +- `c.Expand(`...`)` does modify the value of `c`. This is signified using + `[addr me: Self*]` in the method declaration. + +The pattern '`addr` _patt_' means "first take the address of the argument, which +must be an +[l-value](), and +then match pattern _patt_ against it". + +If the method declaration also includes +[deduced generic parameters](/docs/design/generics/overview.md#deduced-parameters), +the `me` parameter must be in the same list in square brackets `[`...`]`. The +`me` parameter may appear in any position in that list, as long as it appears +after any names needed to describe its type. + +#### Name lookup in member function definitions + +When defining a member function lexically inline, we delay type checking of the +function body until the definition of the current type is complete. This means +that name lookup _for members of objects_ is also delayed. That means that you +can reference `me.F()` in a lexically inline method definition even before the +declaration of `F` in that class definition. However, other names still need to +be declared before they are used. This includes unqualified names, names within +namespaces, and names _for members of types_. + +``` +class Point { + fn Distance[me: Self]() -> f32 { + // ✅ Allowed: `x` and `y` are names for members of an object, + // and so lookup is delayed until `type_of(me) == Self` is complete. + return Math.Sqrt(me.x * me.x + me.y * me.y); + } + + fn CreatePolarInvalid(r: f32, theta: f32) -> Point { + // ❌ Forbidden: unqualified name used before declaration. + return Create(r * Math.Cos(theta), r * Math.Sin(theta)); + } + fn CreatePolarValid1(r: f32, theta: f32) -> Point { + // ❌ Forbidden: `Create` is not yet declared. + return Point.Create(r * Math.Cos(theta), r * Math.Sin(theta)); + } + fn CreatePolarValid2(r: f32, theta: f32) -> Point { + // ❌ Forbidden: `Create` is not yet declared. + return Self.Create(r * Math.Cos(theta), r * Math.Sin(theta)); + } + + fn Create(x: f32, y: f32) -> Point { + // ✅ Allowed: checking that conversion of `{.x: f32, .y: f32}` + // to `Point` is delayed until `Point` is complete. + return {.x = x, .y = y}; + } + + fn CreateXEqualsY(xy: f32) -> Point { + // ✅ Allowed: `Create` is declared earlier. + return Create(xy, xy); + } + + fn CreateXAxis(x: f32) -> Point; + + fn Angle[me: Self]() -> f32; + + var x: f32; + var y: f32; +} + +fn Point.CreateXAxis(x: f32) -> Point; + // ✅ Allowed: `Point` type is complete. + // Members of `Point` like `Create` are in scope. + return Create(x, 0); +} + +fn Point.Angle[me: Self]() -> f32 { + // ✅ Allowed: `Point` type is complete. + // Function is checked immediately. + return Math.ATan2(me.y, me.x); +} +``` + +**Note:** The details of name lookup are still being decided in issue +[#472: Open question: Calling functions defined later in the same file](https://github.com/carbon-language/carbon-lang/issues/472). + +### Nominal data classes + +We will mark [data classes](#data-classes) with an `impl as Data {}` line. + +``` +class TextLabel { + var x: i32; + var y: i32; + + var text: String; + + // This line makes `TextLabel` a data class, which defines + // a number of operations field-wise. + impl as Data {} +} +``` + +The fields of data classes must all be public. That line will add +[field-wise implementations and operations of all interfaces that a struct with the same fields would get by default](#operations-performed-field-wise). + +The word `Data` here refers to an empty interface in the Carbon prologue. That +interface would then be part of our +[strategy for defining how other interfaces are implemented for data classes](#interfaces-implemented-for-data-classes). + +**References:** Rationale for this approach is given in proposal +[#722](/proposals/p0722.md#nominal-data-class). + +### Member type + +Additional types may be defined in the scope of a class definition. + +``` +class StringCounts { + class Node { + var key: String; + var count: i32; + } + var counts: Vector(Node); +} +``` + +The inner type is a member of the type, and is given the name +`StringCounts.Node`. This case is called a _member class_ since the type is a +class, but other kinds of type declarations, like choice types, are allowed. + +### Let + +Other type constants can be defined using a `let` declaration: + +``` +class MyClass { + let Pi:! f32 = 3.141592653589793; + let IndexType:! Type = i32; +} +``` + +The `:!` indicates that this is defining a compile-time constant, and so does +not affect the storage of instances of that class. + +### Alias + +You may declare aliases of the names of class members. This is to allow them to +be renamed in multiple steps or support alternate names. + +``` +class StringPair { + var key: String; + var value: String; + alias first = key; + alias second = value; +} + +var sp1: StringPair = {.key = "K", .value = "1"}; +var sp2: StringPair = {.first = "K", .second = "2"}; +Assert(sp1.first == sp2.key); +Assert(&sp1.first == &sp1.key); +``` + +**Future work:** This needs to be connected to the broader design of aliases, +once that lands. + +### Private access + +By default, all members of a class are fully publicly accessible. Access can be +restricted by adding a keyword, called an +[access modifier](https://en.wikipedia.org/wiki/Access_modifiers), prior to the +declaration. Access modifiers are how Carbon supports +[encapsulation](#encapsulated-types). + +```carbon +class Point { + fn Distance[me: Self]() -> f32; + // These are only accessible to members of `Point`. + private var x: f32; + private var y: f32; +} +``` + +As in C++, `private` means only accessible to members of the class. + +**Future work:** We will add support for `protected` access when inheritance is +added to this design. We will also define a convenient way for tests that belong +to the same library to get access to private members. + +**Future work:** `private` will give the member internal linkage unless it needs +to be external because it is used in an inline method or template. We may in the +future +[add a way to specify internal linkage explicitly](/proposals/p0722.md#specifying-linkage-as-part-of-the-access-modifier). + +**Open questions:** Using `private` to mean "restricted to this class" matches +C++. Other languages support restricting to different scopes: + +- Swift supports "restrict to this module" and "restrict to this file". +- Rust supports "restrict to this module and any children of this module", as + well as "restrict to this crate", "restrict to parent module", and "restrict + to a specific ancestor module". + +**Comparison to other languages:** C++, Rust, and Swift all make class members +private by default. C++ offers the `struct` keyword that makes members public by +default. + +**Rationale:** Carbon makes members public by default for a few reasons: + +- The readability of public members is the most important, since we expect + most readers to be concerned with the public API of a type. +- The members that are most commonly private are the data fields, which have + relatively less complicated definitions that suffer less from the extra + annotation. + +Additionally, there is precedent for this approach in modern object-oriented +languages such as +[Kotlin](https://kotlinlang.org/docs/visibility-modifiers.html) and +[Python](https://docs.python.org/3/tutorial/classes.html), both of which are +well regarded for their usability. + +Keywords controlling visibility are attached to individual declarations instead +of C++'s approach of labels controlling the visibility for all following +declarations to +[reduce context sensitivity](/docs/project/principles/low_context_sensitivity.md). +This matches +[Rust](https://doc.rust-lang.org/reference/visibility-and-privacy.html), +[Swift](https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html), +[Java](http://rosettacode.org/wiki/Classes#Java), +[C#](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers), +[Kotlin](https://kotlinlang.org/docs/visibility-modifiers.html#classes-and-interfaces), +and [D](https://wiki.dlang.org/Access_specifiers_and_visibility). + +**References:** Proposal +[#561: Basic classes](https://github.com/carbon-language/carbon-lang/pull/561) +included the decision that +[members default to publicly accessible](/proposals/p0561.md#access-control) +originally asked in issue +[#665](https://github.com/carbon-language/carbon-lang/issues/665). + +## Future work + +This includes features that need to be designed, questions to answer, and a +description of the provisional syntax in use until these decisions have been +made. + +### Struct literal shortcut + +We could allow you to write `{x, y}` as a short hand for `{.x = x, .y = y}`. ### Optional named parameters @@ -837,16 +1183,6 @@ Some discussion on this topic has occurred in: [2](https://docs.google.com/document/d/1u6GORSkcgThMAiYKOqsgALcEviEtcghGb5TTVT-U-N0/edit) - ["match" in syntax choices doc](https://docs.google.com/document/d/1iuytei37LPg_tEd6xe-O6P_bpN7TIbEjNtFMLYW2Nno/edit#heading=h.y566d16ivoy2) -### Access control - -We will need some way of controlling access to the members of classes. By -default, all members are fully publicly accessible, as decided in -[issue #665](https://github.com/carbon-language/carbon-lang/issues/665). - -The set of access control options Carbon will support is an open question. Swift -and C++ (especially w/ modules) provide a lot of options and a pretty wide space -to explore here. - ### Operator overloading This includes destructors, copy and move operations, as well as other Carbon diff --git a/proposals/README.md b/proposals/README.md index d69805b004a1..468069aea5bc 100644 --- a/proposals/README.md +++ b/proposals/README.md @@ -67,5 +67,6 @@ request: - [0646 - Low context-sensitivity principle](p0646.md) - [0676 - `:!` generic syntax](p0676.md) - [0680 - And, or, not](p0680.md) +- [0722 - Nominal classes and methods](p0722.md) diff --git a/proposals/p0722.md b/proposals/p0722.md new file mode 100644 index 000000000000..8bbbf6bc3846 --- /dev/null +++ b/proposals/p0722.md @@ -0,0 +1,283 @@ +# Nominal classes and methods + + + +[Pull request](https://github.com/carbon-language/carbon-lang/pull/722) + + + +## Table of contents + +- [Problem](#problem) +- [Background](#background) +- [Proposal](#proposal) +- [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) +- [Alternatives considered](#alternatives-considered) + - [Method syntax](#method-syntax) + - [Rationale](#rationale) + - [Full receiver type](#full-receiver-type) + - [Method names line up](#method-names-line-up) + - [Receiver in square brackets](#receiver-in-square-brackets) + - [Receiver parameter is named `me`](#receiver-parameter-is-named-me) + - [Keyword to indicate pass by address](#keyword-to-indicate-pass-by-address) + - [Marking mutating methods at the call site](#marking-mutating-methods-at-the-call-site) + - [Differences between functions and methods](#differences-between-functions-and-methods) + - [Specifying linkage as part of the access modifier](#specifying-linkage-as-part-of-the-access-modifier) + - [Nominal data class](#nominal-data-class) + - [Let constants](#let-constants) + + + +## Problem + +We need facilities for defining new nominal +[record types]() in +Carbon. They will need to support object-oriented features such as methods. + +## Background + +This is a follow up to +[#561: Basic classes: use cases, struct literals, struct types, and future work](https://github.com/carbon-language/carbon-lang/pull/561) +which laid a foundation. + +## Proposal + +The proposal is to update [docs/design/classes.md](docs/design/classes.md) as +described in [this PR](https://github.com/carbon-language/carbon-lang/pull/701). + +## Rationale based on Carbon's goals + +This particular proposal is focusing on [the Carbon +goal](/docs/project/goals.md#code-that-is-easy-to-read-understand-and -write) +that code is "easy to read, understand, and write." Future proposals will +address other aspects of the class type design such as performance. + +## Alternatives considered + +### Method syntax + +The proposed method syntax was decided in question-for-leads issue +[#494: Method syntax](https://github.com/carbon-language/carbon-lang/issues/494). + +A wide variety of alternatives were considered: + +- Using a different introducer such as `method` to distinguish methods from + functions. +- Putting a pattern to match the receiver before the method name. +- Omitting either the receiver name or the receiver type. +- Allowing the user to specify the receiver name instead of using a parameter + named `me` to indicate it is a method. +- Using syntax between `fn` and the method name to indicate that it is a + method, and whether the method takes a value or the address of the receiver. +- Using a keyword to indicate that the first parameter is the receiver, as in + [C++'s "Deducing `this`" proposal](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r6.html). +- Using a delimiter in the parameter list after the receiver type, but the + main choice of `;` seemed pretty subtle. +- Putting the receiver pattern in a separate set of brackets. This could be an + additional set of parens `(`...`)` before the explicit parameter list. Or + the deduced parameters could be put in angle brackets `<`...`>` and the + receiver in square brackes `[`...`]`. + +Examples: + +``` +method (this: Self*) Set(n: Int); +fn [Me*].Set(n: Int); +fn ->Set(n: Int); +fn& Set(n: Int); +fn &Set(s: Self*, n: Int); +fn Set(this self: Self*, n: Int); +fn Set[this self: Self*](n: Int); +fn Set(self: Self*; n: Int); +fn Set(self: Self*)(n: Int); +``` + +Note: These examples are written using the parameter syntax decided in +[issue #542](https://github.com/carbon-language/carbon-lang/issues/542), though +that does not match the syntax used in the discussion at the time. + +#### Rationale + +##### Full receiver type + +We wanted the option to specify the type of the receiver. In C++, the type of +this is controlled by way of special syntax, such as putting a `const` keyword +at the end of the method declaration. We wanted to treat the receiver type more +consistently with other parameter types, like is being considered in +[C++'s "Deducing `this`" proposal](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r6.html). +That proposal also showed the value of including the entire type, rather than +just part such as whether the parameter would be passed by value or address. We +already plan to use this feature to conditionally include methods based on +whether parameters to the type satisfy additional constraints. In this example, +`FixedArray(T, N)` has a `Print()` method if `T` is `Printable`: + +``` +class FixedArray(T:! Type, N:! Int) { + // ... + fn Print[me: FixedArray(P:! Printable, N)]() { ... } +} +``` + +We did consider the option of making the type of the receiver optional. This is +not what we decided to try first, but is an idea we would consider in the +future. + +##### Method names line up + +Using the same introducer `fn` as functions, as decided in +[issue #463](https://github.com/carbon-language/carbon-lang/issues/463), along +with the method name immediately afterwards means that function and method names +line up in the same column visually. + +``` +struct IntContainer { + // Non-methods for building instances + fn MakeFromInts ... -> IntContainer; + fn MakeRepeating ... -> IntContainer; + + // Methods + fn Size ... -> Int; + fn First ... -> Int; + + fn Clear ...; + fn Append ...; +} +``` + +This is to ease scanning and to put the most important information up front. + +We also considered using another 2-character introducer that would distinguish +methods from associated functions, like `me`, but it did not garner much +support. The name `me` was not evocative enough of "method", and people +generally preferred matching other function declarations. We also considered +using different introducers to communicate whether the receiver was passed by +value or by address, for example `ro` for "read-only" and `rw` for "read-write". + +##### Receiver in square brackets + +Putting the receiver pattern inside the explicit parameter list, as is done in +Python, would lead to an unfortunate mismatch between the arguments listed at +the call and the function declaration. That motivated putting the receiver +pattern inside square brackets (`[`...`]`) with deduced parameters. There were +concerns, however, that this parameter did not act like deduced parameters. + +##### Receiver parameter is named `me` + +Having the receiver name be fixed is helpful for consistency. This benefits +readers, and simplfies copying or moving code between functions. A fixed +receiver name also allows us to identify the receiver pattern in the function +declaration, and identify it as a method instead of an ordinary function +associated with the type, like +[static methods in C++](). + +Finally, we wanted the bodies of methods to access members of the object using +an explicit member access. This means we need the name used to access members to +be short, and so we chose the name `me`. + +##### Keyword to indicate pass by address + +We considered using just whether the receiver type was a pointer type to +indicate whether the receiver was passed by address, but we preferred type +information to flow in one direction. Otherwise this would cause a problem if we +want to allow _deduction_ of the object parameter type. This deduction can't be +used to select between pointer and not pointer. + +We also considered supporting reference types that could bind to lvalues without +explicitly taking the address of the object, but references would have added a +lot of complexity to the type system and we believed that they are not otherwise +necessary. + +We decided to adopt an approach where the parameter declaration can contain a +marker for matching an argument's +[value category](https://en.cppreference.com/w/cpp/language/value_category) +separately from its type. This marker would be present for methods that mutate +the object, and so requires that the receiver be an lvalue to match the pattern. +The marker means "first take the address of the argument and then match the rest +of the pattern." We considered a few different possible ways to write this: + +``` +fn Set[&me: Self*](n: Int); +fn Set[*(me: Self*)](n: Int); +fn Set[ref me: Self*](n: Int); +``` + +We eventually settled on using a keyword `addr`. + +``` +fn Set[addr me: Self*](n: Int); +``` + +This doesn't use up a symbol, makes it easier to find in search engines, and +allows us to add more keywords in the future for other calling conventions, such +as `inout`. We may even find that those other keywords have preferable semantics +so we may eventually drop `addr`. + +This keyword approach also is consistent with how we are marking template +parameters with the `template` keyword, as decided in +[issue #565 on generic syntax](https://github.com/carbon-language/carbon-lang/issues/565). + +### Marking mutating methods at the call site + +We considered making it visible at the call site when the receiver was passed by +address, with the address-of operator `&`. + +``` +(&x).Set(4); +``` + +This would in effect make the mutating methods be methods on the pointer type +rather than the class type. + +### Differences between functions and methods + +Question-for-leads issue +[#494: Method syntax](https://github.com/carbon-language/carbon-lang/issues/494) +also considered what would be different between functions and methods: + +- Methods use a different calling syntax: `x.F(n)`. +- Methods distinguish between taking the receiver (`x`) by value or by + pointer, without changing the call syntax. +- Methods have to be declared in the body of the class. +- Methods and associated functions both have access to private members of the + class. +- Only methods can opt in to using dynamic dispatch. +- The receiver parameter to a method varies covariantly in inheritance unlike + other parameter types. + +### Specifying linkage as part of the access modifier + +We considered various access modifiers such as `internal` or `private.internal` +that would enforce internal linkage in addition to restricted visibility. We +decided to postpone such considerations for the time being. + +Even if we _do_ need explicit controls here, we did not feel the need to +front-load that complexity right now and with relatively less time or experience +to evaluate the tradeoffs. If and when linkage becomes a visible issue and +important to discuss, we can add it. Until then, we can see how much mileage we +can get out of purely implementation techniques. + +### Nominal data class + +We needed some way of opting a nominal class into the fieldwise behavior of a +data class. [In Kotlin](https://kotlinlang.org/docs/data-classes.html), you +precede the `class` declaration with a `data` keyword. Carbon already has a way +of marking types as having specific semantic properties, implementing +interfaces. This leverages the support for interfaces in the language to be able +to express things like "here is a blanket implementation of an interface for all +data classes" in a consistent way. + +We chose the name `Data` rather than `DataClass` for the interface since tuples +implicitly implement the interface and were "product types" rather than +"classes". + +### Let constants + +We wanted a consistent interpretation for `let` declarations in classes and +function bodies. Experience from C++ `const int` variables, where we try +constant-evaluation and then give the program different semantics based on +whether such evaluation happened to work, suggested we wanted to instead require +`:!` in all places where a value that's usable during compilation is introduced.