Principle: All APIs are library APIs (#1280)

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
Geoff Romer
2022-06-13 13:10:43 -07:00
committed by GitHub
co-authored by Chandler Carruth Richard Smith
parent 504a4b1364
commit bc40bdc279
2 changed files with 188 additions and 0 deletions
@@ -0,0 +1,104 @@
# Principle: All APIs are library APIs
<!--
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)
- [Applications of this principle](#applications-of-this-principle)
- [Exceptions](#exceptions)
- [Alternatives considered](#alternatives-considered)
<!-- tocstop -->
## Background
Every major modern programming language comes with a standard library, which
consists of APIs that are not part of the core language, but instead are written
in the language (although their implementations may not be). However, different
languages draw the boundary between language and library in different places.
For example, Go's `map` type is built into the core language, whereas the C++
equivalent, `std::unordered_map`, is part of the standard library. In Swift,
even fundamental types like integers and pointers are part of the standard
library; there are no truly "built in" types.
These decisions can have important consequences for the design of the language.
For example, many important features of C++, such as move semantics, variadics,
and coroutines, were motivated largely by their anticipated uses in a small set
of standard library types. In a language with a different design philosophy,
those types could have been built into the core language. This would probably
have substantially simplified the language, and made those types available
faster. However, that would have come at the cost of less flexibility for users
outside the common case.
## Principle
In Carbon, every public function is declared in some Carbon `api` file, and
every public `interface`, `impl`, and first-class type is defined in some Carbon
`api` file. In some cases, the bodies of public functions will not be defined as
Carbon code, or will be defined as hybrid Carbon code using intrinsics that
aren't available to ordinary Carbon code. However, we will try to minimize those
situations.
Thus, even "built-in" APIs can be used like user-defined APIs, by importing the
appropriate library and using qualified names from that library, relying on the
ordinary semantic rules for Carbon APIs.
## Applications of this principle
We expect Carbon to have a special "prelude" library that is implicitly imported
by all Carbon source files, and there might be a special name lookup rule to
allow the names in the prelude to be used unqualified. However, in accordance
with this principle, they will remain available to ordinary qualified name
lookup as well.
According to the resolutions of
[#543](https://github.com/carbon-language/carbon-lang/issues/543) and
[#750](https://github.com/carbon-language/carbon-lang/issues/543), Carbon will
have a substantial number of type keywords, such as `i32`, `f64`, and `bool`.
However, these keywords will all be aliases for ordinary type names, such as
`Carbon.Int(32)`, `Carbon.Float(64)`, and `Carbon.Bool`. Furthermore, all
arithmetic and logical operators will be overloadable, so that those types can
be defined as class types. The member function bodies for these types will be
probably not be implemented in Carbon, but this principle applies only to
function declarations, not function definitions.
Similarly, a pointer type such as `Foo*` will be an alias for some library class
type, for example `Carbon.Ptr(Foo)`. As a result, Carbon will support
overloading pointer operations like `->` and unary `*`.
All Carbon operations that use function-style syntax, such as `sizeof()` and
`decltype()` in C++, will be standard library functions. As above, in some cases
we may choose to alias those functions with keywords, and the function bodies
may not be defined in Carbon.
## Exceptions
This principle applies to types only if they are _first-class_, meaning that
they can be the types of run-time variables, function parameters, and return
values. Carbon's type system will probably also include some types whose usage
is more restricted, and this principle will not apply to them. Most importantly,
function types might not be first-class types, in which case they need not be
library types.
The logic for translating a literal expression to a value of the appropriate
type is arguably part of that type's public API, but will not be part of that
type's class definition.
Tuple types will probably not fully conform to this principle, because doing so
would be circular: there is no way to name a tuple type that doesn't rely on
tuple syntax, and no way to define a class body for a tuple type that doesn't
contain tuple patterns. However, we will strive to ensure that it is possible to
define a parameterized class type within Carbon that supports all the same
operations as built-in tuple types.
## Alternatives considered
- [Built-in primitive types](/proposals/p1280.md#built-in-primitive-types)
+84
View File
@@ -0,0 +1,84 @@
# Principle: All APIs are library APIs
<!--
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/1280)
<!-- toc -->
## Table of contents
- [Problem](#problem)
- [Background](#background)
- [Proposal](#proposal)
- [Details](#details)
- [Rationale](#rationale)
- [Alternatives considered](#alternatives-considered)
- [Built-in primitive types](#built-in-primitive-types)
<!-- tocstop -->
## Problem
We need a clear and consistent "division of labor" between the core language and
the standard library.
## Background
See
[the principle doc](/docs/project/principles/library_apis_only.md#background).
See also [#57](https://github.com/carbon-language/carbon-lang/pull/57), a
previous iteration of a similar idea.
## Proposal
In Carbon, every public function will be declared in some Carbon `api` file, and
every public `interface`, `impl`, and first-class type will be defined in some
Carbon `api` file. In some cases, the bodies of public functions will not be
defined as Carbon code, or will be defined as hybrid Carbon code using
intrinsics that aren't available to ordinary Carbon code. However, we will try
to minimize those situations.
Thus, even "built-in" APIs can be used like user-defined APIs, by importing the
appropriate library and using qualified names from that library, relying on the
ordinary semantic rules for Carbon APIs.
## Details
See [the principle doc](/docs/project/principles/library_apis_only.md).
## Rationale
This principle facilitates
[software evolution](/docs/project/goals.md#software-and-language-evolution), by
helping to ensure that code written in terms of a Carbon-provided type can be
migrated to use a suitable user-defined type instead. It also facilitates
evolution of the language itself, by enabling more of that evolution to take
place in library code, which doesn't require compiler expertise.
This principle helps make Carbon code
[easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write),
because user-defined APIs can match the ergonomics of language-defined APIs, and
the syntax, language rules, and core concepts are consistent between the two.
This principle indirectly helps Carbon support
[performance-critical software](/docs/project/goals.md#performance-critical-software):
by using Carbon's API abstraction mechanisms for even the most fundamental
types, we ensure that those mechanisms do not impose any performance overhead.
## Alternatives considered
### Built-in primitive types
We could follow the general outline of C++, where arithmetic and pointer types
are built-in. However, that would substantially erode the advantages outlined
above. We expect Carbon to have multiple kinds of pointers (for example, to
represent different kinds of ownership), and multiple kinds of arithmetic types
(for example, to handle overflow in different ways). They can't all be built-in,
so putting even the common-case types in the library helps ensure that Carbon
has enough expressive power for the uncommon-case library types.