Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Naming conventions #861

Merged
merged 21 commits into from
Jan 27, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 216 additions & 0 deletions proposals/p0861.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
# Naming conventions

<!--
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/861)

<!-- toc -->

## Table of contents

- [Problem](#problem)
- [Background](#background)
- [Cross-language precedent](#cross-language-precedent)
- [Carbon artifacts](#carbon-artifacts)
- [Proposal](#proposal)
- [Details](#details)
- [Constants](#constants)
- [Carbon-provided item naming](#carbon-provided-item-naming)
- [Open questions](#open-questions)
- [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals)
- [Alternatives considered](#alternatives-considered)
- [Other naming conventions](#other-naming-conventions)
- [Other conventions for naming Carbon types](#other-conventions-for-naming-carbon-types)

<!-- tocstop -->

## Problem

The goal of this proposal is to establish naming conventions for:

- Idiomatic Carbon code.
- Carbon-provided features, including:
- Keywords, such as `fn` or `for`.
- Type literals, such as `i32`.
- Types, such as `bool` or `String`.

The reason for resolving this through proposal is to ensure we're headed in a
reasonably consistent path as we write early documentation and example code.

## Background

### Cross-language precedent

Naming conventions are an issue frequently addressed by style guides. A few
examples of language-provided guidelines are:

- [Effective Go](https://golang.org/doc/effective_go#names)
- [Python's PEP 8](https://www.python.org/dev/peps/pep-0008/#naming-conventions)
- Note PEP 8 offers a list of
[naming styles](https://www.python.org/dev/peps/pep-0008/#descriptive-naming-styles).
- [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/naming.html)
- [Swift's API Design Guidelines](https://swift.org/documentation/api-design-guidelines/#general-conventions)

### Carbon artifacts

Related issues:

- Issue
[#543: pick names for fixed-size integer types](https://github.com/carbon-language/carbon-lang/issues/543)
covers numeric type literal naming.
- Issue
[#750: Naming conventions for Carbon-provided features](https://github.com/carbon-language/carbon-lang/issues/750)
was used for initial naming convention discussion.

Related proposals:

- Proposal
[#720: Property naming in C++](https://github.com/carbon-language/carbon-lang/pull/720)
covers nuances of property naming.

## Proposal

Only `UpperCamelCase` and `lower_snake_case` conventions will be used, in order
to minimize the variation in rules.

- For idiomatic Carbon code:
- `UpperCamelCase` will be used when the named entity cannot have a
dynamically varying value. For example, functions, namespaces, or
compile-time constant values.
- `lower_snake_case` will be used when the named entity's value won't be
known until runtime, such as for variables.
- For Carbon-provided features:
- Keywords and type literals will use `lower_snake_case`.
- Other code will use the guidelines for idiomatic Carbon code.

In other words:
chandlerc marked this conversation as resolved.
Show resolved Hide resolved

| Item | Convention | Explanation |
| ------------------------- | ------------------ | ------------------------------------------------------------------------------------------ |
| Packages | `UpperCamelCase` | Used for compile-time lookup. |
| Types | `UpperCamelCase` | Resolved at compile-time. |
| Functions | `UpperCamelCase` | Resolved at compile-time. |
| Methods | `UpperCamelCase` | Methods, including virtual methods, are equivalent to functions. |
| Generic parameters | `UpperCamelCase` | May vary based on inputs, but are ultimately resolved at compile-time. |
| Compile-time constants | `UpperCamelCase` | Resolved at compile-time. See [constants](#constants) for more remarks. |
| Variables | `lower_snake_case` | May be reassigned and thus require runtime information. |
| Member variables | `lower_snake_case` | Behave like variables. |
| Keywords | `lower_snake_case` | Special, and developers can be expected to be comfortable with this casing cross-language. |
| Type literals | `lower_snake_case` | Equivalent to keywords. |
| Boolean type and literals | `lower_snake_case` | Equivalent to keywords. |
| Other Carbon types | `UpperCamelCase` | Behave like normal types. |
| `Self` and `Base` | `UpperCamelCase` | These are similar to type members on a class. |

## Details

### Constants

Supposing `let` might be used to designate a constant, consider the following
code:

```carbon
package Example;

let CompileTimeConstant: i32 = 7;

fn RuntimeFunction(runtime_constant: i32);
```

In this example, `CompileTimeConstant` has a singular value (`7`) which is known
at compile-time. As such, it uses `UpperCamelCase`.

On the other hand, `runtime_constant` may be constant within the function body,
but it is assigned at runtime when `RuntimeFunction` is called. Its value is
only known in a given runtime invocation of `RuntimeFunction`. As such, it uses
`lower_snake_case`.

### Carbon-provided item naming

Carbon-provided items are split into a few categories:

- Keywords; for example, `for`, `fn`, and `var`
- Type literals; for example, `i<digits>`, `u<digits>`, and `f<digits>`
- Boolean type and literals; for example, `bool`, `true`, and `false`
- The separate categorization of booleans should not be taken as a rule
that only booleans would use lowercase; it's just the only example right
now.
- `Self` and `Base`
- Other Carbon types; for example, `Int`, `UInt`, and `String`

Note that while other Carbon types currently use `UpperCamelCase`, that should
not be inferred to mean that future Carbon types will do the same. The leads
will make decisions on future naming.

## Open questions

## Rationale based on Carbon's goals

- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write)
- The intent is that limiting to `UpperCamelCase` and `lower_snake_case`
will offer developers a limited number of style rules to learn.
- There is a desire to maintain good ergonomics for developers.
- [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code)
- There is a general desire to maintain naming consistency with C++, and
this can be seen with keywords such as `for` as well as booleans.

## Alternatives considered

### Other naming conventions

A couple naming conventions that were discussed briefly are:

- `lowerCamelCase` names came up, but are hard to distinguish from
`lower_snake_case` for single-word identifiers. By contrast,
`UpperCamelCase` and `lower_snake_case` are distinct, whether a single word
or multiple.
- `ALL_CAPS_SNAKE_CASE` is used in C++ code, such as for macros and
compile-time constants. With Carbon, we hope the language is simple enough
that the readability benefit of an additional naming convention wouldn't
outweigh the cost of giving developers more naming conventions to learn.

### Other conventions for naming Carbon types

In detail, individual naming decisions that had alternative patterns or options
discussed were:

- Type literals use `lower_snake_case` as described in issue
[#543: pick names for fixed-size integer types](https://github.com/carbon-language/carbon-lang/issues/543).
- `i8` is more ergonomic than `Int8` for brevity.
- `i32` and `i64` maintain the same length as C++'s `int`.
- We don't want to use `I32` because `I32` can be hard to visually
distinguish with `132` or `l32`, even though it may help screen readers.
- Booleans use `bool`, `true`, and `false` mainly because there's a desire not
to skew from C++.
- While leads are okay with some things being shadowed, such as `Self` and
`Base`, there is a desire not to allow shadowing of booleans.
- `Self` and `Base` are `UpperCamelCase` because leads want them to look more
like normal types.
- They may be implemented as a hybrid approach, using something similar to
lookup in classes so they aren't quite keywords, but somewhat similar to
keywords because leads may want to reject them as identifiers in
non-class contexts.
- `String` and potentially other names follow idiomatic naming conventions.
- Note that, in this case, divergence from C++ is accepted.

Taking that into consideration, and that we are likely to keep
`lower_snake_case` for keywords and type literals in particular, a couple
options discussed would have mainly affect `self`, `base`, and `string`:

- Anything language-provided without needing an `import` is always
`lower_snake_case`.
- `i32`, `bool`, `true`, `self`, `base`, `string.is_empty()`
- As above, but prelude class members follow standard naming conventions.
- `i32`, `bool`, `true`, `self`, `base`, `string.IsEmpty()`

The understanding is that the difference between `bool` being treated similarly
to a keyword and `Self` being treated similarly to a type (both regardless of
implementation) is somewhat arbitrary. The decision not to use
`lower_snake_case` consistently depends mainly on the leaning of the leads, that
`self` and `base` felt like they shouldn't strictly be treated as keywords, and
`string` felt like the point where users should expect something more like an
idiomatic class. Future borderline cases may need to be decided by leads whether
they should be treated as more keyword-like or type-like.