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 or class_), 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.
  • 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 as True).
  • 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, and crate 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 and base 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 syntax r#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 named r#Core is an unrelated package, and Core.foo always refers to members of the predeclared Core 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.

Alternatives considered