Files
carbon-lang/docs/design/classes.md
T
36764ff1af Basic classes: use cases, struct literals, struct types, and future work (#561)
This proposal defines the very basics of `class` types, primarily focused on:

-   use cases including: data classes, encapsulated types, inheritance with and without `virtual`, interfaces as base classes, and mixins for code reuse;
-   anonymous data types for called _structural data classes_ or _struct types_. Struct literals are used to initialize class values and ad-hoc parameter and return types with named components; and
-   future work, including the provisional syntax already in use for features that have not been decided.

The intent is to both make some small incremental progress and get agreement on direction. As such it doesn't include things like nominal types, methods, access control, inheritance, etc.

It proposes this struct type and literal syntax:
```
var p: {.x: Int, .y: Int} = {.x = 0, .y = 1};
```
Note that it uses commas (`,`) between fields instead of semicolons (`;`), and no introducer for types or literal values.

Incorporates decisions from #665 , #653 , #651


Co-authored-by: Geoff Romer <gromer@google.com>
Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
2021-08-09 12:28:36 -07:00

1005 lines
43 KiB
Markdown

# Classes
<!--
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
-->
<!-- toc -->
## Table of contents
- [Overview](#overview)
- [Use cases](#use-cases)
- [Data classes](#data-classes)
- [Encapsulated types](#encapsulated-types)
- [Without inheritance](#without-inheritance)
- [With inheritance and subtyping](#with-inheritance-and-subtyping)
- [Polymorphic types](#polymorphic-types)
- [Interface as base class](#interface-as-base-class)
- [Non-polymorphic inheritance](#non-polymorphic-inheritance)
- [Interop with C++ multiple inheritance](#interop-with-c-multiple-inheritance)
- [Mixins](#mixins)
- [Background](#background)
- [Overview](#overview-1)
- [Members](#members)
- [Data members have an order](#data-members-have-an-order)
- [Struct types](#struct-types)
- [Literals](#literals)
- [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)
- [Self](#self)
- [Let](#let)
- [Methods](#methods)
- [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)
- [Mixins](#mixins-1)
- [Non-virtual inheritance](#non-virtual-inheritance)
- [Memory layout](#memory-layout)
- [No `static` variables](#no-static-variables)
- [Computed properties](#computed-properties)
- [Interfaces implemented for data classes](#interfaces-implemented-for-data-classes)
<!-- tocstop -->
## Overview
A Carbon `class` is a user-defined
[record type](<https://en.wikipedia.org/wiki/Record_(computer_science)>). This
is the primary mechanism for users to define new types in Carbon. A `class` has
members that are referenced by their names, in contrast to a
[Carbon tuple](tuples.md) which defines a
[product type](https://en.wikipedia.org/wiki/Product_type) whose members are
referenced positionally.
Carbon supports both named, or "nominal", and unnamed, anonymous, or
"structural", class types. Nominal class types are all distinct, but structural
types are equal if they have the same sequence of member types and names.
Structural class literals may be used to initialize or assign values to nominal
class variables.
## Use cases
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.
### Data classes
Data classes are types that consist of data fields that are publicly accessible
and directly read and manipulated by client code. They have few if any methods,
and generally are not involved in inheritance at all.
Examples include:
- a key and value pair returned from a `SortedMap` or `HashMap`
- a 2D point that might be used in a rendering API
Properties:
- Operations like copy, move, destroy, unformed, and so on are defined
field-wise.
- Anonymous classes types and literals should match data class semantics.
Expected in idiomatic Carbon-only code.
**Background:** Kotlin has a dedicated concise syntax for defining
[_data classes_](https://kotlinlang.org/docs/data-classes.html) that avoids
boilerplate. Python has a
[data class library](https://docs.python.org/3/library/dataclasses.html),
proposed in [PEP 557](https://www.python.org/dev/peps/pep-0557/), that fills a
similar role.
### Encapsulated types
There are several categories of types that support
[encapsulation](<https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)>).
This is done by making their data fields private so access and modification of
values are all done through methods defined on the type.
#### Without inheritance
The common case for encapsulated types are those that do not participate in
inheritance. These types neither support being inherited from (they are
["final"](<https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)#Non-subclassable_classes>))
nor do they extend other types.
Examples of this use case include:
- strings, containers, iterators
- types with invariants such as `Date`
- RAII types that are movable but not copyable like C++'s `std::unique_ptr` or
a file handle
- non-movable types like `Mutex`
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.
#### With inheritance and subtyping
The [subtyping](https://en.wikipedia.org/wiki/Subtyping) you get with
inheritance is that you may assign the address of an object of a derived type to
a pointer to its base type. For this to work, the compiler needs implementation
strategies that allow operations performed through the pointer to the base type
work independent of which derived type it actually points to. These strategies
include:
- Arranging for the the data layout of derived types to start with the data
layout of the base type as a prefix.
- Putting a pointer to a table of function pointers, a
[_vtable_](https://en.wikipedia.org/wiki/Virtual_method_table), as the first
data member of the object. This allows methods to be
[_virtual_](https://en.wikipedia.org/wiki/Virtual_function) and have a
derived-type-specific implementation, an _override_, that is used even when
invoking the method on a pointer to a base type.
- Non-virtual methods implemented on a base type should be applicable to all
derived types. In general, derived types should not attempt to overload or
override non-virtual names defined in the base type.
Note that these subtyping implementation strategies generally rely on
encapsulation, but encapsulation is not a strict requirement in all cases.
This subtyping relationship also creates safety concerns, which Carbon should
protect against.
[Slicing problems](https://en.wikipedia.org/wiki/Object_slicing) can arise when
the source or target of an assignment is a dereferenced pointer to the base
type. It is also incorrect to delete an object with a non-virtual destructor
through a pointer to a base type.
##### Polymorphic types
Carbon will fully support single-inheritance type hierarchies with polymorphic
types.
Polymorphic types support
[dynamic dispatch](https://en.wikipedia.org/wiki/Dynamic_dispatch) using a
[vtable](https://en.wikipedia.org/wiki/Virtual_method_table), and data members,
but only single inheritance. Individual methods opt in to using dynamic
dispatch, so types will have a mix of
["virtual"](https://en.wikipedia.org/wiki/Virtual_function) and non-virtual
methods. Polymorphic types support traditional
[object-oriented single inheritance](<https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)>),
a mix of [subtyping](https://en.wikipedia.org/wiki/Subtyping) and
[implementation and code reuse](<https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)#Code_reuse>).
We exclude complex multiple inheritance schemes, virtual inheritance, and so on
from this use case. This is to avoid the complexity and overhead they bring,
particularly since the use of these features in C++ is generally discouraged.
The rule is that every type has at most one base type with data members for
subtyping purposes. Carbon will support additional base types as long as they
[don't have data members](#interface-as-base-class) or
[don't support subtyping](#mixins).
**Background:**
[The "Nothing is Something" talk by Sandi Metz](https://www.youtube.com/watch?v=OMPfEXIlTVE)
and
[the Composition Over Inheritance Principle](https://python-patterns.guide/gang-of-four/composition-over-inheritance/)
describe design patterns to use instead of multiple inheritance to support types
that vary over multiple axes.
In rare cases where the complex multiple inheritance schemes of C++ are truly
needed, they can be effectively approximated using a combination of these
simpler building blocks.
Polymorphic types support a number of different kinds of methods:
- They will have virtual methods:
- Polymorphic types will typically include virtual destructors.
- The virtual methods types may have default implementations or be
[_abstract_](<https://en.wikipedia.org/wiki/Method_(computer_programming)#Abstract_methods>)
(or
[_pure virtual_](https://en.wikipedia.org/wiki/Virtual_function#Abstract_classes_and_pure_virtual_functions)).
In the latter case, they must be implemented in any derived class that
can be instantiated.
- Virtual methods may be
[_protected_](https://en.wikipedia.org/wiki/Access_modifiers) or
[_private_](https://stackoverflow.com/questions/2170688/private-virtual-method-in-c),
intended to be called by methods in the base type but implemented in the
descendant.
- They may have non-virtual public or private helper methods, like
[encapsulated types without inheritance](#without-inheritance). These avoid
the overhead of a virtual function call, and can be written when the base
class has sufficient data members.
- They may have protected helper methods, typically non-virtual, provided by
the base type to be called by the descendant.
Note that there are two uses for protected methods: those implemented in the
base and called in the descendant, and the other way around.
["The End Of Object Inheritance & The Beginning Of A New Modularity" talk by Augie Fackler and Nathaniel Manista](https://www.youtube.com/watch?v=3MNVP9-hglc)
discusses design patterns that split up types to reduce the number of kinds of
calls between base and derived types, and make sure calls only go in one
direction.
We expect polymorphic types in idiomatic Carbon-only code, at least for the
medium term. Extending this design to support polymorphic types is future work.
###### Interface as base class
We distinguish the specific case of polymorphic base classes that have no data
members:
- From an implementation perspective, the lack of data members removes most of
the problems with supporting multiple inheritance.
- They are about decoupling two pieces of code instead of collaborating.
- As a use case, they are used primarily for subtyping and much less
implementation reuse than other polymorphic types.
- This case overlaps with the
[interface](/docs/design/generics/terminology.md#interface) concept
introduced for [Carbon generics](/docs/design/generics/overview.md).
Removing support for data fields greatly simplifies supporting multiple
inheritance. For example, it removes the need for a mechanism to figure out the
offset of those data fields in the object. Similarly we don't need
[C++'s virtual inheritance](https://en.wikipedia.org/wiki/Virtual_inheritance)
to avoid duplicating those fields. Some complexities still remain, such as
pointers changing values when casting to a secondary parent type, but these seem
manageable given the benefits of supporting this useful case of multiple
inheritance.
While an interface base class is generally for providing an API that allows
decoupling two pieces of code, a polymorphic type is a collaboration between a
base and derived type to provide some functionality. This is a bit like the
difference between a library and a framework, where you might use many of the
former but only one of the latter.
Interface base classes are primarily used for subtyping. The extent of
implementation reuse is generally limited by the lack of data members, and the
decoupling role they play is usually about defining an API as a set of public
pure-virtual methods. Compared to other polymorphic types, they more rarely have
methods with implementations (virtual or not), or have methods with restricted
access. The main use case is when there is a method that is implemented in terms
of pure-virtual methods. Those pure-virtual methods may be marked as protected
to ensure they are only called through the non-abstract API, but can still be
implemented in descendants.
While it is typical for this case to be associated with single-level inheritance
hierarchies, there are some cases where there is an interface at the root of a
type hierarchy and polymorphic types as interior branches of the tree. The case
of generic interfaces extending or requiring other interface would also be
modeled by deeper inheritance hierarchies.
An interface as base class needs to either have a virtual destructor or forbid
deallocation.
There is significant overlap between interface base classes and
[Carbon interfaces](generics/overview.md#interfaces). Both represent APIs as a
collection of method names and signatures to implement. The subset of interfaces
that support dynamic dispatch are called _object-safe_, following
[Rust](https://doc.rust-lang.org/reference/items/traits.html#object-safety):
- They don't have a `Self` in the signature of a method in a contravariant
position like a parameter.
- They don't have free associated types or other associated items used in a
method signature.
The restrictions on object-safe interfaces match the restrictions on base class
methods. The main difference is the representation in memory. A type extending a
base class with virtual methods includes a pointer to the table of methods in
the object value itself, while a type implementing an interface would store the
pointer alongside the pointer to the value in a `DynPtr(MyInterface)`. Of
course, the interface option also allows the method table to be passed at
compile time.
**Note:** This presumes that we include some concept of `final` methods in
interfaces to match non-virtual functions in base classes.
We expect idiomatic Carbon-only code to generally use Carbon interfaces instead
of interface base classes. We may still support interface base classes long term
if we determine that the ability to put the pointer to the method
implementations in the object value is important for users, particularly with a
single parent as in the [polymorphic type case](#polymorphic-types). Extending
this design to support interface base classes is future work.
**Background:**
[C++ abstract base classes](https://en.wikipedia.org/wiki/Abstract_type) that
don't have data members and
[Java interfaces](<https://en.wikipedia.org/wiki/Interface_(Java)>) model this
case.
##### Non-polymorphic inheritance
While it is not common, there are cases where C++ code uses inheritance without
dynamic dispatch or a
[vtable](https://en.wikipedia.org/wiki/Virtual_method_table). Instead, methods
are never overridden, and derived types only add data and methods. There are
some cases where this is done in C++ but would be done differently in Carbon:
- For implementation reuse without subtyping, Carbon code should use mixins or
composition. Carbon won't support private inheritance.
- Carbon will allow data members to have size zero, so the
[empty-base optimization](https://en.cppreference.com/w/cpp/language/ebo) is
unnecessary.
- For cases where the derived type does not add any data members, in Carbon
you can potentially use adapter types instead of inheritance.
However, there are still some cases where non-virtual inheritance makes sense.
One is a parameterized type where a prefix of the data is the same independent
of the parameter. An example of this is containers with a
[small-buffer optimization](https://akrzemi1.wordpress.com/2014/04/14/common-optimizations/#sbo),
as described in the talk
[CppCon 2016: Chandler Carruth "High Performance Code 201: Hybrid Data Structures"](https://www.youtube.com/watch?v=vElZc6zSIXM).
By moving the data and methods that don't depend on the buffer size to a base
class, we reduce the instantiation overhead for monomorphization. The base type
is also useful for reducing instantiation for consumers of the container, as
long as they only need to access methods defined in the base.
Another case for non-virtual inheritance is for different node types within a
data structure that have some data members in common. This is done in LLVM's
map,
[red-black tree](https://github.com/llvm-mirror/libcxx/blob/master/include/__tree),
and list data structure types. In a linked list, the base type might have the
next and previous pointers, which is enough for a sentinel node, and there would
also be a derived type with the actual data member. The base type can define
operations like "splice" that only operate on the pointers not the data, and
this is in fact enforced by the type system. Only the derived node type needs to
be parameterized by the element type, saving on instantiation costs as before.
Many of the concerns around non-polymorphic inheritance are the same as for the
non-virtual methods of [polymorphic types](#polymorphic-types). Assignment and
destruction are examples of operations that need particular care to be sure they
are only done on values of the correct type, rather than through a subtyping
relationship. This means having some extrinsic way of knowing when it is safe to
downcast before performing one of those operations, or performing them on
pointers that were never upcast to the base type.
##### Interop with C++ multiple inheritance
While Carbon won't support all the C++ forms of multiple inheritance, Carbon
code will still need to interoperate with C++ code that does. Of particular
concern are the `std::iostream` family of types. Most uses of those types are
the input and output variations or could be migrated to use those variations,
not the harder bidirectional cases.
Much of the complexity of this interoperation could be alleviated by adopting
the restriction that Carbon code can't directly access the fields of a virtual
base class. In the cases where such access is needed, the workaround is to
access them through C++ functions.
We do not expect idiomatic Carbon-only code to use multiple inheritance.
Extending this design to support interopating with C++ types using multiple
inheritance is future work.
### Mixins
A [mixin](https://en.wikipedia.org/wiki/Mixin) is a declaration of data,
methods, and interface implementations that can be added to another type, called
the "main type". The methods of a mixin may also use data, methods, and
interface implementations provided by the main type. Mixins are designed around
implementation reuse rather than subtyping, and so don't need to use a vtable.
A mixin might be an implementation detail of a [data class](#data-classes),
[object type](#object-types), or
[derived type of a polymorphic type](#polymorphic-types). A mixin might
partially implement an [interface as base class](#interface-as-base-class).
**Examples:**
[intrusive linked list](https://www.boost.org/doc/libs/1_63_0/doc/html/intrusive.html),
intrusive reference count
In both of these examples, the mixin needs the ability to convert between a
pointer to the mixin's data (like a "next" pointer or reference count) and a
pointer to the containing object with the main type.
Mixins are expected in idiomatic Carbon-only code. Extending this design to
support mixins is future work.
**Background:** Mixins are typically implemented using the
[curiously recurring template pattern](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)
in C++, but other languages support them directly.
- In Dart, the mixin defines an interface that the destination type ends up
implementing, which restores a form of subtyping. See
[Dart: What are mixins?](https://medium.com/flutter-community/dart-what-are-mixins-3a72344011f3).
- Swift is considering
[a proposal to add mixin support](https://github.com/Anton3/swift-evolution/blob/mixins/proposals/NNNN-mixins.md).
## Background
See how other languages tackle this problem:
- [Swift](https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html)
- has two different concepts: classes support
[inheritance](https://docs.swift.org/swift-book/LanguageGuide/Inheritance.html)
and use
[reference counting](https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html)
while structs have value semantics
- may have
[constructor functions called "initializers"](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html)
and
[destructors called "deinitializers"](https://docs.swift.org/swift-book/LanguageGuide/Deinitialization.html)
- supports
[properties](https://docs.swift.org/swift-book/LanguageGuide/Properties.html),
including computed & lazy properties
- methods are const by default
[unless marked mutating](https://docs.swift.org/swift-book/LanguageGuide/Methods.html#ID239)
- supports
[extensions](https://docs.swift.org/swift-book/LanguageGuide/Extensions.html)
- has per-field
[access control](https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html)
- [Rust](https://doc.rust-lang.org/book/ch05-01-defining-structs.html)
- has no support for inheritance
- has no special constructor functions, instead has literal syntax
- has some convenience syntax for common cases:
[variable and field names matching](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#using-the-field-init-shorthand-when-variables-and-fields-have-the-same-name),
[updating a subset of fields](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax)
- [can have unnamed fields](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#using-tuple-structs-without-named-fields-to-create-different-types)
- [supports structs with size 0](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#unit-like-structs-without-any-fields)
- [Zig](https://ziglang.org/documentation/0.6.0/#struct)
- [explicitly mark structs as packed to manually control layout](https://ziglang.org/documentation/0.6.0/#packed-struct)
- has a struct literal syntax,
[including for anonymous structs](https://ziglang.org/documentation/0.6.0/#Anonymous-Struct-Literals)
- no special constructor functions
- supports fields with undefined values
- supports structs with size 0
- supports generics by way of memoized compile time functions accepting
and returning types
- [supports default field values](https://ziglang.org/documentation/0.6.0/#toc-Default-Field-Values)
- [has no properties or operator overloading -- Zig does not like hidden control flow](https://ziglang.org/#Small-simple-language)
## Overview
Beyond tuples, Carbon allows defining
[record types](<https://en.wikipedia.org/wiki/Record_(computer_science)>). This
is the primary mechanism for users to extend the Carbon type system and is
deeply rooted in C++ and its history (C and Simula). We call them _classes_
rather than other terms as that is both familiar to existing programmers and
accurately captures their essence: they define the types of objects with
(optional) support for methods, encapsulation, and so on.
A class type defines the interpretation of the bytes of a value of that type,
including the size, data members, and layout. It defines the operations that may
be performed on those values, including what methods may be called. A class type
may directly have constant members. The type itself is a compile-time immutable
constant value.
## Members
The members of a class are named, and are accessed with the `.` notation. For
example:
```
var p: Point2D = ...;
// Data member access
p.x = 1;
p.y = 2;
// Method call
Print(p.DistanceFromOrigin());
```
[Tuples](tuples.md) are used for cases where accessing the members positionally
is more appropriate.
### Data members have an order
The data members of a class, or _fields_, have an order that matches the order
they are declared in. This determines the order of those fields in memory, and
the order that the fields are destroyed when a value goes out of scope or is
deallocated.
## Struct types
_Structural data classes_, or _struct types_, are convenient for defining
[data classes](#data-classes) in an ad-hoc manner. They would commonly be used:
- as the return type of a function that returns multiple values and wants
those values to have names so a [tuple](tuples.md) is inappropriate
- as an initializer for other `class` variables or values
- as a type parameter to a container
Note that struct types are examples of _data class types_ and are still classes,
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.
### Literals
_Structural data class literals_, or _struct literals_, are written using this
syntax:
```
var kvpair: auto = {.key = "the", .value = 27};
```
This produces a struct value with two fields:
- The first field is named "`key`" and has the value `"the"`. The type of the
field is set to the type of the value, and so is `String`.
- The second field is named "`value`" and has the value `27`. The type of the
field is set to the type of the value, and so is `Int`.
Note: A comma `,` may optionally be included after the last field:
```
var kvpair: auto = {.key = "the", .value = 27,};
```
**Open question:** To keep the literal syntax from being ambiguous with compound
statements, Carbon will adopt some combination of:
- looking ahead after a `{` to see if it is followed by `.name`;
- not allowing a struct literal at the beginning of a statement;
- only allowing `{` to introduce a compound statement in contexts introduced
by a keyword where they are required, like requiring `{ ... }` around the
cases of an `if...else` statement.
### Type expression
The type of `kvpair` in the last example would be represented by this
expression:
```
{.key: String, .value: Int}
```
This syntax is intended to parallel the literal syntax, and so uses commas (`,`)
to separate fields instead of a semicolon (`;`) terminator. This choice also
reflects the expected use inline in function signature declarations.
Struct types may only have data members, so the type declaration is just a list
of field names and types. The result of a struct type expression is an immutable
compile-time type value.
Note: Like with struct literal expressions, a comma `,` may optionally be
included after the last field:
```
{.key: String, .value: Int,}
```
Also note that `{}` represents both the empty struct literal and its type.
### Assignment and initialization
When initializing or assigning a variable with a data class such as a struct
type to a struct value on the right hand side, the order of the fields does not
have to match, just the names.
```
var different_order: {.x: Int, .y: Int} = {.y = 2, .x = 3};
Assert(different_order.x == 3);
Assert(different_order.y == 2);
```
**Open question:** What operations and in what order happen for assignment and
initialization?
- Is assignment just destruction followed by initialization? Is that
destruction completed for the whole object before initializing, or is it
interleaved field-by-field?
- When initializing to a literal value, is a temporary containing the literal
value constructed first or are the fields initialized directly? The latter
approach supports types that can't be moved or copied, such as mutex.
- Perhaps some operations are _not_ ordered with respect to each other?
When initializing or assigning, the order of fields is determined from the
target on the left side of the `=`. This rule matches what we expect for classes
with encapsulation more generally.
### Operations performed field-wise
Generally speaking, the operations that are available on a data class value,
such as a value with a struct type, are dependent on those operations being
available for all the types of the fields.
For example, two values of the same data class type may be compared for equality
if equality is supported for every member of the type:
```
var p: auto = {.x = 2, .y = 3};
Assert(p == {.x = 2, .y = 3});
Assert(p != {.x = 2, .y = 4});
```
Similarly, a data class has an unformed state if all its members do. Treatment
of unformed state follows
[#257](https://github.com/carbon-language/carbon-lang/pull/257).
`==` and `!=` are defined on a data class type if all its field types support
it:
```
Assert({.x = 2, .y = 4} != {.x = 5, .y = 3});
```
**Open question:** Which other comparisons are supported is the subject of
[question-for-leads issue #710](https://github.com/carbon-language/carbon-lang/issues/710).
```
// Illegal
Assert({.x = 2, .y = 3} != {.y = 4, .x = 5});
```
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
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.
### Nominal class types
The declarations for nominal class types will have a different format.
Provisionally we have been using something like this:
```
class TextLabel {
var x: Int;
var y: Int;
var text: String;
}
```
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.
### Construction
There are a variety of options for constructing class values, we might choose to
support, including initializing from struct values:
```
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);
```
### Member type
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);
}
```
The inner type is a member of the type, and is given the name
`StringCounts.Node`.
### 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.
```
class IntListNode {
var data: Int;
var next: IntListNode*;
}
```
An equivalent definition of `IntListNode`, since `Self` is an alias for the
current type, is:
```
class IntListNode {
var data: Int;
var next: Self*;
}
```
`Self` refers to the innermost type declaration:
```
class IntList {
class IntListNode {
var data: Int;
var next: Self*;
}
var first: IntListNode*;
}
```
### Let
Other type constants can provisionally be defined using a `let` declaration:
```
class MyClass {
let Pi: Float32 = 3.141592653589793;
let IndexType: Type = Int;
}
```
There are definite questions about this syntax:
- 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.
### Methods
A future proposal will incorporate
[method](<https://en.wikipedia.org/wiki/Method_(computer_programming)>)
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:
- 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](<https://en.wikipedia.org/wiki/Static_(keyword)#Static_method>),
are written: `fn Create() -> Self { ... }`
We do not expect to have implicit member access in methods, so inside the method
body members will be accessed through the `me` parameter.
### Optional named parameters
Structs are being considered as a possible mechanism for implementing optional
named parameters. We have three main candidate approaches: allowing struct types
to have field defaults, having dedicated support for destructuring struct values
in pattern contexts, or having a dedicated optional named parameter syntax.
#### Field defaults for struct types
If struct types could have field defaults, you could write a function
declaration with all of the optional parameters in an option struct:
```
fn SortIntVector(
v: Vector(Int)*,
options: {.stable: Bool = false,
.descending: Bool = false} = {}) {
// Code using `options.stable` and `options.descending`.
}
// Uses defaults of `.stable` and `.descending` equal to `false`.
SortIntVector(&v);
SortIntVector(&v, {});
// Sets `.stable` option to `true`.
SortIntVector(&v, {.stable = true});
// Sets `.descending` option to `true`.
SortIntVector(&v, {.descending = true});
// Sets both `.stable` and `.descending` options to `true`.
SortIntVector(&v, {.stable = true, .descending = true});
// Order can be different for arguments as well.
SortIntVector(&v, {.descending = true, .stable = true});
```
#### Destructuring in pattern matching
We might instead support destructuring struct patterns with defaults:
```
fn SortIntVector(
v: Vector(Int)*,
{stable: Bool = false, descending: Bool = false}) {
// Code using `stable` and `descending`.
}
```
This would allow the same syntax at the call site, but avoids
[some concerns with field defaults](https://github.com/carbon-language/carbon-lang/pull/561#discussion_r683856715)
and allows some other use cases such as destructuring return values.
#### Discussion
We might support destructuring directly:
```
var {key: String, value: Int} = ReturnKeyValue();
```
or by way of a mechanism that converts a struct into a tuple:
```
var (key: String, value: Int) =
ReturnKeyValue().extract(.key, .value);
// or maybe:
var (key: String, value: Int) =
ReturnKeyValue()[(.key, .value)];
```
Similarly we might support optional named parameters directly instead of by way
of struct types.
Some discussion on this topic has occurred in:
- [question-for-leads issue #505 on named parameters](https://github.com/carbon-language/carbon-lang/issues/505)
- labeled params brainstorming docs
[1](https://docs.google.com/document/d/1a1wI8SHGh3HYV8SUWPIKhg48ZW2glUlAMIIS3aec5dY/edit),
[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
operators such as `+` and `/`. We expect types to implement these operations by
implementing corresponding interfaces, see
[the generics overview](generics/overview.md).
### Inheritance
Carbon will need ways of saying:
- this `class` type has a virtual method table
- this `class` type extends a base type
- this `class` type is "final" and may not be extended further
- this method is "virtual" and may be overridden in descendents
- this method is "pure virtual" or "abstract" and must be overridden in
descendants
- this method overrides a method declared in a base type
Multiple inheritance will be limited in at least a couple of ways:
- At most one supertype may define data members.
- Carbon types can't access data members of C++ virtual base classes.
There is a
[document considering the options for constructing objects with inheritance](https://docs.google.com/document/d/1GyrBIFyUbuLJGItmTAYUf9sqSDQjry_kjZKm5INl-84/edit).
### C++ abstract base classes interoperating with object-safe interfaces
We want four things so that Carbon's object-safe interfaces may interoperate
with C++ abstract base classes without data members, matching the
[interface as base class use case](#interface-as-base-class):
- Ability to convert an object-safe interface (a type-of-type) into an
C++-compatible base class (a base type), maybe using
`AsBaseClass(MyInterface)`.
- Ability to convert a C++ base class without data members (a base type) into
an object-safe interface (a type-of-type), maybe using `AsInterface(MyIBC)`.
- Ability to convert a (thin) pointer to an abstract base class to a `DynPtr`
of the corresponding interface.
- Ability to convert `DynPtr(MyInterface)` values to a proxy type that extends
the corresponding base class `AsBaseType(MyInterface)`.
Note that the proxy type extending `AsBaseType(MyInterface)` would be a
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.
### Mixins
We will need some way to declare mixins. This syntax will need a way to
distinguish defining versus requiring member variables. Methods may additionally
be given a default definition but may be overridden. Interface implementations
may only be partially provided by a mixin. Mixin methods will need to be able to
convert between pointers to the mixin type and the main type.
Open questions include whether a mixin is its own type that is a member of the
containing type, and whether mixins are templated on the containing type. Mixins
also complicate how constructors work.
### Non-virtual inheritance
We need some way of addressing two safety concerns created by non-virtual
inheritance:
- Unclear how to safely run the right destructor.
- [Object slicing](https://en.wikipedia.org/wiki/Object_slicing) is a danger
when dereferencing a pointer of a base type.
These concerns would be resolved by distinguishing between pointers that point
to a specified type only and those that point to a type or any subtype. The
latter case would have restrictions to prevent misuse. This distinction may be
more complexity than is justified for a relatively rare use case. An alternative
approach would be to forbid destruction of non-final types without virtual
destructors, and forbid assignment of non-final types entirely.
This open question is being considered in
[question-for-leads issue #652](https://github.com/carbon-language/carbon-lang/issues/652).
### Memory layout
Carbon will need some way for users to specify the memory layout of class types
beyond simple ordering of fields, such as controlling the packing and alignment
for the whole type or individual members.
### No `static` variables
At the moment, there is no proposal to support
[`static` member variables](https://en.wikipedia.org/wiki/Class_variable#Static_member_variables_and_static_member_functions),
in line with avoiding global variables more generally. Carbon may need some
support in this area, though, for parity with and migration from C++.
### Computed properties
Carbon might want to support members of a type that are accessed like a data
member but return a computed value like a function. This has a number of
implications:
- It would be a way of publicly exposing data members for
[encapsulated types](#encapsulated-types), allowing for rules that otherwise
forbid mixing public and private data members.
- It would provide a more graceful evolution path from a
[data class](#data-classes) to an [encapsulated type](#encapsulated-types).
- It would give an option to start with a [data class](#data-classes) instead
of writing all the boilerplate to create an
[encapsulated type](#encapsulated-types) preemptively to allow future
evolution.
- It would let you take a variable away and put a property in its place with
no other code changes. The number one use for this is so you can put a
breakpoint in the property code, then later go back to public variable once
you understand who was misbehaving.
- We should have some guidance for when to use a computed property instead of
a function with no arguments. One possible criteria is when it is a pure
function of the state of the object and executes in an amount of time
similar to ordinary member access.
However, there are likely to be differences between computed properties and
other data members, such as the ability to take the address of them. We might
want to support "read only" data members, that can be read through the public
api but only modified with private access, for data members which may need to
evolve into a computed property. There are also questions regarding how to
support assigning or modifying computed properties, such as using `+=`.
### Interfaces implemented for data classes
We should define a way for defining implementations of interfaces for struct
types. To satisfy coherence, these implementations would have to be defined in
the library with the interface definition. The syntax might look like:
```
interface ConstructWidgetFrom {
fn Construct(Self) -> Widget;
}
external impl {.kind: WidgetKind, .size: Int}
as ConstructWidgetFrom { ... }
```
In addition, we should define a way for interfaces to define templated blanket
implementations for [data classes](#data-classes) more generally. These
implementations will typically subject to the criteria that all the data fields
of the type must implement the interface. An example use case would be to say
that a data class is serializable if all of its fields were. For this we will
need a type-of-type for capturing that criteria, maybe something like
`DataFieldsImplement(MyInterface)`. The templated implementation will need some
way of iterating through the fields so it can perform operations fieldwise. This
feature should also implement the interfaces for any tuples whose fields satisfy
the criteria.
It is an open question how define implementations for binary operators. For
example, if `Int` is comparable to `Float32`, then `{.x = 3, .y = 2.72}` should
be comparable to `{.x = 3.14, .y = 2}`. The trick is how to declare the criteria
that "`T` is comparable to `U` if they have the same field names in the same
order, and for every field `x`, the type of `T.x` implements `ComparableTo` for
the type of `U.x`."