Principle: Namespace cleanliness
Table of contents
Background
Names and entities in a program can come from multiple sources – from a local declaration, from an import, from the standard library, or from the prelude. Names can be imported from Carbon code or imported or derived from code written in another language such as C++ or an interface description language such as that of Protobuf, MIDL, or CORBA. Names can be selected for use in a program that language designers later decide they want to use as keywords. And in order to use a library, it is sometimes necessary to redeclare the same name that that library chose.
This puts a lot of pressure on the language to support a free choice of naming for entities. Different languages make different choices in this space:
- Many languages have a set of keywords that are not usable as identifiers, with no workaround. If this set collides with a name needed by user code, the user is left to solve this problem, often by rewriting the identifier in some way (
klass
orclass_
), which sometimes conflicts with the general naming convention used by the code. And conversely, suboptimal choices are made for new language keywords to avoid causing problems for existing code. - C and C++ reserve a family of identifiers, such as those beginning with an underscore and a capital letter. However, it’s not clear which audiences the reserved identifiers are for, and this leads to collisions between standard library vendors and compiler authors, as well as between implementation extensions and language extensions.
- MSVC provides a
__identifier(keyword)
extension that allows using a keyword as an identifier. This extension is also implemented by Clang in-fms-extensions
mode. - GCC provides an
__asm__(symbol)
extension that allows a specific symbol to be assigned to an object or function, which provides ABI compatibility but not source compatibility with code that uses a keyword as a symbol name. This extension is also implemented by Clang.
- MSVC provides a
- Python reserves some identifiers but still allows them to be freely overwritten (such as
bool
) and reserves some identifiers but rejects assignment to them (such asTrue
). - Rust provides a raw identifier syntax to allow most identifiers with reserved meaning to be used by a program, but not all:
self
,Self
,super
,extern
, andcrate
cannot be used as raw identifiers. Rust also predeclares a large number of library names in every file, but allows them to be shadowed by user declarations with the same name. - Swift provides a raw identifier syntax using backticks:
`class`
, and is considering extending this to allow arbitrary non-word-shaped character sequences between the`
s.
Carbon provides raw identifier syntax, for example r#for
, to allow using keywords as identifiers. Carbon also intends to have strict shadowing rules that may make predeclared identifiers that are not keywords difficult or impossible to redeclare and use in inner scopes.
Principle
In Carbon, the language does not encroach on the developer’s namespace. There are no predeclared or reserved identifiers. In cases where the language gives special meaning to a word or to word-shaped syntax such as i32
, that special meaning can always be undone with raw identifier syntax, r#
.
Conversely, when adding language keywords, we will not select an inferior keyword merely to avoid the risk of breaking existing programs. We will still take into account how often it is desirable to use the word as an identifier, including in domain-specific contexts, because that is a factor in whether it would make a good keyword, and will manage the rollout of new keywords to make it straightforward to migrate existing uses to r#
or a different name.
Applications of this principle
- Words like
final
andbase
that only have special meaning in a few contexts, and could otherwise be made available as identifiers, are keywords in Carbon.{.base = ...}
and{.r#base = ...}
specify different member names. - Words like
self
that are declared by the developer but nonetheless have special language-recognized meaning are keywords in Carbon.[self:! Self]
introduces a self parameter;[r#self:! Self]
introduces a deduced parameter. - Words like
Self
that are implicitly declared by the language in some contexts are keywords, even though we could treat them as user-declared identifiers that are merely implicitly declared in some cases. - Words like
i32
that are treated as type literals rather than keywords can be used as identifiers with raw identifier syntaxr#i32
. - There are no predeclared identifiers imported from the prelude. If an entity is important enough to be available by default, we should add a keyword, and allow the name of the entity to be used for other purposes with
r#
. - The predeclared package name
Core
is a keyword. A package namedr#Core
is an unrelated package, andCore.foo
always refers to members of the predeclaredCore
package.
Exceptions
For now, we reserve the package names Main
and Cpp
. These names aren’t predeclared in any scope, and the name Main
is not even usable from within source files to refer to the main package. However, there is currently no way to avoid collisions between the package name Cpp
and a top-level entity named Cpp
if they are both used in the same source file.