mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 17:00:25 +01:00
Principle: One static open extension mechanism (#998)
This is a proposal to adopt interfaces as the single static open extension mechanism for things like selecting how operators are implemented for types. Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
committed by
GitHub
co-authored by
Richard Smith
parent
1b4c81fa69
commit
00a178769f
@@ -0,0 +1,89 @@
|
||||
# Principle: One static open extension mechanism
|
||||
|
||||
<!--
|
||||
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
|
||||
|
||||
- [Background](#background)
|
||||
- [Principle](#principle)
|
||||
- [Alternatives considered](#alternatives-considered)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
## Background
|
||||
|
||||
In C++, a single function may be overloaded with definitions in multiple files.
|
||||
The [ADL](https://en.wikipedia.org/wiki/Argument-dependent_name_lookup) name
|
||||
lookup rule even allows an unqualified call to resolve to functions defined in
|
||||
different namespaces. These rules are used to define extension points with
|
||||
static dispatch for operator overloading and functions like
|
||||
[`swap`](https://www.cplusplus.com/reference/algorithm/swap/).
|
||||
|
||||
Nothing in C++ restricts the signatures of function overloads. This means that
|
||||
if overloading is used as an extension point to define an operation for a
|
||||
variety of types, there is no way to type check generic code that tries to
|
||||
invoke that operation over those types.
|
||||
|
||||
Further, in C++, all non-member functions can be found in this way, if they are
|
||||
declared in the same namespace as a type that could be associated with an
|
||||
argument list in a call. There is no straightforward opt-out mechanism in a
|
||||
function declaration, and while there are opt-out mechanisms at call sites, they
|
||||
are rarely used. As a consequence, many non-member functions with the same name
|
||||
can form part of an overload set, even if they provide unrelated functionality,
|
||||
and there is no indication in the code of which functions in different
|
||||
namespaces are intended to expose the same capability.
|
||||
|
||||
## Principle
|
||||
|
||||
In Carbon, [`interface`s](/docs/design/generics/overview.md) are the only static
|
||||
open extension mechanism.
|
||||
|
||||
Each type may define its own implementation of each interface. Generic code can
|
||||
be written that works with any type implementing the interface. That code can be
|
||||
type checked independent of which type the generic code is instantiated with by
|
||||
using the fact that the interface specifies the signatures of the calls.
|
||||
|
||||
To keep the language simple, this is the only static open extension mechanism in
|
||||
Carbon. This means that function overloading is limited in Carbon to only
|
||||
signatures defined together in the same library. It also means that to
|
||||
interoperate with C++, the operators and `swap` need to have corresponding
|
||||
interfaces on the Carbon side.
|
||||
|
||||
The main advantage of interfaces as an open extension mechanism over open
|
||||
overloading is allowing generics to be type checked separately. In addition,
|
||||
they are less [context sensitive](low_context_sensitivity.md). Generics are
|
||||
[coherent](/docs/design/generics/terminology.md#coherence), while open function
|
||||
overloading can resolve names differently depending on what is imported. Closed
|
||||
overloading in Carbon also simplifies what gets exported to C++ from Carbon.
|
||||
Interface implementations express intent by being explicit, in contrast to how
|
||||
adding a function to a cross-file overload set can be accidental.
|
||||
|
||||
Interfaces provide an way to group functions together, and express the
|
||||
constraint that all of the functions in the group are implemented. Consider a
|
||||
random-access iterator, which has a number of methods. If a C++ template
|
||||
function only accesses some of those methods which happens to match the subset
|
||||
defined for a type, the code will work temporarily but fail later when the code
|
||||
is changed to use a different subset.
|
||||
|
||||
This helps achieve the Carbon Goal of
|
||||
[code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write).
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
Another approach to operator overloading is to use methods with a specific name.
|
||||
In C++ these start with the
|
||||
[`operator` keyword](https://en.cppreference.com/w/cpp/language/operators).
|
||||
[Python uses method with names starting and ending with double underscores](https://docs.python.org/3/reference/datamodel.html#special-method-names).
|
||||
Interfaces are more flexible about where implementations may be defined. For
|
||||
example, with special method names, `+` on a `Vector(T)` class could only be
|
||||
defined as part of the `Vector(T)` definition. With interfaces, additionally `+`
|
||||
for `Vector(MyType)` could be implemented with `MyType`. C++ provides this
|
||||
flexibility by also permitting non-method operator overloads, but this brings
|
||||
with it the cost of selecting a best-matching operator from a potentially very
|
||||
large open overload set.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Principle: One static open extension mechanism
|
||||
|
||||
<!--
|
||||
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/998)
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Problem](#problem)
|
||||
- [Proposal](#proposal)
|
||||
- [Details](#details)
|
||||
- [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals)
|
||||
- [Alternatives considered](#alternatives-considered)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
## Problem
|
||||
|
||||
There are a few ways of approaching open extension, such as defining how
|
||||
operators are overload for every type. For Carbon, with its
|
||||
[focus on performance](/docs/project/goals.md#performance-critical-software), we
|
||||
are particularly interested in those that support static dispatch, to avoid the
|
||||
runtime overhead of dynamic dispatch. The three main options are:
|
||||
|
||||
- Open function overloading, where new overloads for a given name can be
|
||||
defined broadly, as is done in C++.
|
||||
- Interfaces, as is done in
|
||||
[Rust](https://doc.rust-lang.org/rust-by-example/trait/ops.html).
|
||||
- Special method names, as are used in
|
||||
[C++](https://en.cppreference.com/w/cpp/language/operators) and
|
||||
[Python](https://docs.python.org/3/reference/datamodel.html#special-method-names).
|
||||
|
||||
We would prefer to use a single mechanism, if possible, for simplicity.
|
||||
|
||||
## Proposal
|
||||
|
||||
Proposal is to use interfaces as the single open extension mechanism.
|
||||
|
||||
## Details
|
||||
|
||||
Details are in the added principle doc:
|
||||
[docs/project/principles/static_open_extension.md](docs/project/principles/static_open_extension.md).
|
||||
|
||||
## Rationale based on Carbon's goals
|
||||
|
||||
This proposal is pursuing Carbon's goal of having
|
||||
[code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write).
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
Early arguments for this approach were put forth in
|
||||
[this document](https://docs.google.com/document/d/1uvX_hmw5DVs1SFjnehUUizGnI6C099vqIGfUhfCwBIo/edit#).
|
||||
Reference in New Issue
Block a user