Просмотр исходного кода

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>
josh11b 4 лет назад
Родитель
Сommit
00a178769f
2 измененных файлов с 147 добавлено и 0 удалено
  1. 89 0
      docs/project/principles/static_open_extension.md
  2. 58 0
      proposals/p0998.md

+ 89 - 0
docs/project/principles/static_open_extension.md

@@ -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.

+ 58 - 0
proposals/p0998.md

@@ -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#).