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

Code and name organization #107

Merged
merged 101 commits into from
Oct 14, 2020
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
101 commits
Select commit Hold shift + click to select a range
2ae6b6f
Merge pull request #1 from carbon-language/master
jonmeow May 20, 2020
5bba3a8
Merge remote-tracking branch 'upstream/master'
jonmeow May 20, 2020
98562a6
Merge remote-tracking branch 'upstream/master'
jonmeow May 21, 2020
a05e9c3
Merge pull request #2 from carbon-language/master
jonmeow Jun 1, 2020
dfe42cb
Merge pull request #3 from carbon-language/master
jonmeow Jun 3, 2020
3931951
Merge pull request #4 from carbon-language/master
jonmeow Jun 4, 2020
43cfde9
Merge pull request #5 from carbon-language/master
jonmeow Jun 10, 2020
b489c23
Merge pull request #6 from carbon-language/master
jonmeow Jun 12, 2020
5b10b4d
Merge pull request #7 from carbon-language/master
jonmeow Jun 12, 2020
6ce0f97
Merge remote-tracking branch 'upstream/master'
jonmeow Jun 16, 2020
218df9f
Merge remote-tracking branch 'upstream/master'
jonmeow Jun 22, 2020
5c33fbf
Merge remote-tracking branch 'upstream/master'
jonmeow Jun 22, 2020
160b5c4
Merge remote-tracking branch 'upstream/master'
jonmeow Jun 2, 2020
5829ac9
Merge remote-tracking branch 'upstream/trunk' into trunk
jonmeow Jul 6, 2020
8692c9a
Creating new proposal: Code and name organization
jonmeow Jul 8, 2020
6b7f469
Filling out template with PR 107
jonmeow Jul 8, 2020
654cad6
First draft for external
jonmeow Jul 8, 2020
aa96aaf
Merge
jonmeow Jul 31, 2020
ca5d5bd
Shift focus from files/libraries/packages to the code organization
jonmeow Aug 4, 2020
c132018
Rewriting
jonmeow Aug 5, 2020
3d99524
Drafting
jonmeow Aug 7, 2020
5813814
Progress
jonmeow Aug 11, 2020
3154969
Updates
jonmeow Aug 12, 2020
a546035
Updates
jonmeow Aug 12, 2020
1222334
Updates
jonmeow Aug 12, 2020
3cebea3
Updates
jonmeow Aug 12, 2020
4bbba84
Updates
jonmeow Aug 12, 2020
d7d2850
Extend justification
jonmeow Aug 12, 2020
7c4c32c
Updates
jonmeow Aug 12, 2020
d5b8ab9
Refactoring
jonmeow Aug 12, 2020
3e15730
Updates
jonmeow Aug 12, 2020
e578b1f
Updates
jonmeow Aug 12, 2020
5319498
Updates
jonmeow Aug 12, 2020
06dea5d
Apply suggestions from code review
jonmeow Aug 13, 2020
bf74c75
Updates
jonmeow Aug 13, 2020
9559bec
Merge branch 'proposal-code-and-name-organi' of https://github.com/jo…
jonmeow Aug 13, 2020
af6f4b1
Updates
jonmeow Aug 13, 2020
2311f03
Updates
jonmeow Aug 14, 2020
db2d550
Examples
jonmeow Aug 14, 2020
1ebecc1
A couple more examples
jonmeow Aug 14, 2020
54f740b
Updates
jonmeow Aug 14, 2020
cf53d74
Updates
jonmeow Aug 14, 2020
0d81ff4
Apply suggestions from code review
jonmeow Aug 15, 2020
03f92f8
Updates
jonmeow Aug 15, 2020
a5c4e16
Merge branch 'proposal-code-and-name-organi' of https://github.com/jo…
jonmeow Aug 15, 2020
8d62937
Prettier
jonmeow Aug 15, 2020
1d09728
Updates
jonmeow Aug 17, 2020
9fed213
Updates
jonmeow Aug 17, 2020
b53a98d
Updates
jonmeow Aug 17, 2020
50cb15b
Updates
jonmeow Aug 17, 2020
1e55c4f
Updates
jonmeow Aug 17, 2020
365a089
Updates
jonmeow Aug 17, 2020
0c6c6de
Updates
jonmeow Aug 17, 2020
8297dbe
Updates
jonmeow Aug 17, 2020
eae6a9b
Updates
jonmeow Aug 17, 2020
2111545
Apply suggestions from code review
jonmeow Aug 18, 2020
23bee19
Merge branch 'trunk' into proposal-code-and-name-organi
jonmeow Aug 18, 2020
94bd816
Updates
jonmeow Aug 18, 2020
69d1893
Updates
jonmeow Aug 19, 2020
4f10ddb
Updates
jonmeow Aug 19, 2020
e56549e
Updates
jonmeow Aug 19, 2020
1cec071
TODOS
jonmeow Aug 20, 2020
99801c1
Apply suggestions from code review
jonmeow Aug 20, 2020
91f989e
Updates
jonmeow Aug 21, 2020
559677b
Merge
jonmeow Aug 24, 2020
7c464a5
Updates
jonmeow Aug 27, 2020
e1c46e3
Updates
jonmeow Aug 27, 2020
60dfa21
Updates
jonmeow Aug 27, 2020
c8ea9bb
Updates
jonmeow Aug 27, 2020
578591b
Updates
jonmeow Aug 27, 2020
84cd55f
Updates
jonmeow Aug 31, 2020
882d5e0
Updates
jonmeow Aug 31, 2020
7a0a400
Updates
jonmeow Aug 31, 2020
f914c1b
Merge branch 'trunk' into proposal-code-and-name-organi
jonmeow Sep 1, 2020
d0ba98a
Address geoffromer comment
jonmeow Sep 3, 2020
b8f9fc2
Updates
jonmeow Sep 3, 2020
53a1072
Apply suggestions from code review
jonmeow Sep 8, 2020
0da274b
Updates
jonmeow Sep 8, 2020
16ba315
Merge branch 'trunk' into proposal-code-and-name-organi
jonmeow Sep 9, 2020
687c166
progress
jonmeow Sep 9, 2020
31a7619
Remove foo uses
jonmeow Sep 9, 2020
6efd78e
Merge branch 'trunk' into proposal-code-and-name-organi
jonmeow Sep 9, 2020
4d15065
Link
jonmeow Sep 10, 2020
441fe24
Updates
jonmeow Sep 11, 2020
6d9f997
Updates
jonmeow Sep 11, 2020
d9a7b34
Updates
jonmeow Sep 14, 2020
71375af
Apply suggestions from code review
jonmeow Sep 15, 2020
6c4d9e3
Merge branch 'trunk' into proposal-code-and-name-organi
jonmeow Sep 15, 2020
9607815
Update code_and_name_organization.md
jonmeow Sep 15, 2020
06b31f5
Updates
jonmeow Sep 16, 2020
8a5b20b
Software as wording
jonmeow Sep 16, 2020
79270d3
Updates
jonmeow Sep 17, 2020
754cb6b
Updates
jonmeow Sep 17, 2020
0388e5b
Updates
jonmeow Sep 17, 2020
3c52bb7
Updates
jonmeow Sep 18, 2020
2a1a662
Updates
jonmeow Sep 21, 2020
e9f4404
New open question from discussion with josh11b
jonmeow Sep 29, 2020
3ef6558
Update docs/design/code_and_name_organization.md
jonmeow Oct 6, 2020
f71c05d
Updates
jonmeow Oct 6, 2020
cd6e7b4
Merge branch 'proposal-code-and-name-organi' of https://github.com/jo…
jonmeow Oct 6, 2020
fe4c7d2
Minor adjustment
jonmeow Oct 6, 2020
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
1 change: 1 addition & 0 deletions proposals/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ request:
- [Decision](p0044-decision.md)
- [0074 - Change comment/decision timelines in proposal process](p0074.md)
- [Decision](p0074-decision.md)
- [0107 - Code and name organization](p0107.md)

<!-- endproposals -->
358 changes: 358 additions & 0 deletions proposals/p0107.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,358 @@
# Code and name organization

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

## Table of contents

<!-- toc -->

- [Overview](#overview)
- [Files](#files)
- [Libraries](#libraries)
- [Namespaces](#namespaces)
- [Packages](#packages)
- [Imports](#imports)
- [Shadowing of names](#shadowing-of-names)
- [Standard library names](#standard-library-names)

<!-- tocstop -->

## Overview
jonmeow marked this conversation as resolved.
Show resolved Hide resolved

Carbon code is organized into files, libraries, and packages in increasing
scope. Names within Carbon code are organized into a hierarchy of named scopes,
the outermost of which are namespaces. A quick overview of these terms will be
useful before we describe them in detail.

**File:** This is the basic unit of Carbon compilation. Each file can be
compiled separately (but with access to imports), and parsed (without semantic
analysis) in isolation. These correspond to translation units in (modular) C++.
For example, the
`[flat_hash_map.h](https://github.com/abseil/abseil-cpp/blob/master/absl/container/flat_hash_map.h)`
header would be a single module interface unit with C++ modules, and it would be
a single file in Carbon.

**Library:** This is the basic unit of Carbon interfaces that can be imported.
In other words, they form the link-time abstraction boundary. A library consists
jonmeow marked this conversation as resolved.
Show resolved Hide resolved
of one or more Carbon files. These correspond exactly to C++ modules or Bazel
`cc_library` targets. For example, if all the code in the Abseil
`[container](https://abseil.io/docs/cpp/guides/container)` library were in a
single `cc_library`, that library would map to a Carbon library (and C++
module).

**Namespace:** This is the basic unit organizing Carbon names and represents the
_name_ abstraction boundary. They introduce (potentially nested) named scopes.
jonmeow marked this conversation as resolved.
Show resolved Hide resolved
They can be navigated using dot-syntax (`Foo.Bar.Baz`). Libraries, notably, are
not namespaces and are orthogonal to them.

**Package:** This is a collection of one or more Carbon libraries that share a
top-level namespace. They don't introduce new constructs, but rather are a
restricted and special kind of namespace. All Carbon code is in a named package
and cannot be in a "global" namespace. Neither libraries nor (nested) namespaces
can span package boundaries. Typically, all the libraries within a package are
developed and distributed together. These are expected to correspond to a
repository on GitHub or a top level namespace in an organization's codebase. For
example, Abseil would likely map to a Carbon package much like it resides in the
top level C++ namespace `::absl`.

This organization is specifically designed to scale up to relatively complex
structures with multiple files in a library and multiple libraries in a package.
However, it also keeps simple cases simple. For example, when a package consists
of a single library, or a library consists of a single file. The goal is to
jonmeow marked this conversation as resolved.
Show resolved Hide resolved
avoid unnecessary syntax and ceremony in these cases while having consistent and
graceful scaling up of syntax to support the more complex cases.

## Files

All Carbon files are part of exactly one library (and all libraries are part of
exactly one package). There are also two kinds of files: ones that contribute to
jonmeow marked this conversation as resolved.
Show resolved Hide resolved
the interface of a library, and ones that only contain implementation details of
that library. The latter cannot export any interfaces from the library, and that
kind is designed to facilitate more efficient parallel and distributed
compilation strategies.

The first (non-comment, non-whitespace) line of a Carbon file declares what
library and package the file is a part of, as well as whether the file is
implementation-only:

```
package Abseil library Container;
```

```
package Abseil library Container impl;
```

Here, `package`, `library`, and `impl` are all keywords (at least in this
context). The package name is `Abseil`, and the library name is `Container`.

There are some more details of these file declarations that will be explained as
we get into the details of Carbon libraries.

## Libraries

Carbon's libraries are based very directly on the design of C++ modules.

Every Carbon library has a primary file that defines its exported interface.
This file must start off with the canonical form of the top-level declaration:

```
package Abseil library Container;
```

The library may contain additional files as part of its implementation. These
all must start with a special top-level declaration:

```
package Abseil library Container impl;
```

Within a library, files can import other files from that library using a special
syntax (see below). Every file in a library which exports an interface to users
of the library must be transitively imported into the primary interface file and
only those files transitively imported are allowed to export an interface. Files
which are not transitively imported into the primary interface file (and thus
also do not export any interfaces) are allowed to import the primary file of the
library in addition to importing other implementation files. Imports must always
form a directed acyclic graph (DAG), including these file-based imports.

**Open question:** We may find that it is too burdensome to insist on the imports within a library forming a DAG and/or allow cyclic references within files and libraries. Relaxing this would add complexity to the compilation (no trivial parallel or incremental compiles between files within a library) but allow a simpler intra-library import model where we simply list the files providing exported interfaces in the main file. Then the interface is just the concatenation of those files, and the implementation is the further concatenation of the rest of the files. We're starting with the more restrictive model in part because it seems easier to relax this later if desired.

The result is that the set of files which must be examined to find the complete
exported interface of a library is the transitive closure of imported files
starting from the primary interface file.

## Namespaces

Carbon namespaces work essentially the same way as C++ namespaces. They form
named scopes for names. They can nest and be reopened and aren't restricted to
library boundaries. A good example in practice will be things like `testing` or
`internal` namespaces below a package. These often will have names that benefit
from an isolated space but are spread across several libraries.

Namespaces are navigated using dot notation just like names in other scopes in
Carbon:

```
namespace Foo {
namespace Bar {
namespace Baz {
alias ??? MyInt = Int;
}
}
}

fn F(Foo.Bar.Baz.MyInt x);
```

**Note**: the syntax for aliases is not at all in a good state yet. We've considered a few alternatives, but they all end up being confusing in some way. We need to figure out a good and clean syntax that can be used here.

There are no wildcard aliases (or complex partial wildcard aliases like
`using namespace foo;` in C++). Alias declarations can alias namespace names, so
there is no need for custom namespace alias support.

**Possible extension**: If it proves to be a common case, we could support
aliasing a set of names in one namespace into another and provide special syntax
for it. However, it would force us to have syntax introducing many (top-level)
names in a single declaration, and presents other syntactic challenges. Unclear
this will be worth it or how best to design it. Very vague syntax sketch:

```
// Normal alias of a namespace.
alias Foo.Bar.Baz: FBB;

namespace FBB {
alias Int: OtherInt;
}

fn G(Foo.Bar.Baz.OtherInt y);

namespace Elsewhere {
// Quickly create aliases in this namespace for two names
// (`MyInt`, and `OtherInt`) in some other namespace.
alias ??? from Foo.Bar.Baz ??? MyInt, OtherInt;
}
```

## Packages

Carbon's packages are specialized top-level namespaces. All code in Carbon is
written within a package and thus within a top-level namespace (and thus
inherently not in the global namespace). Libraries cannot span packages. This
restricts our library abstraction boundary to fit within the top-level name
abstraction boundary. This is the only place where we intersect our name
abstractions and our library abstractions.

All names introduced in a Carbon file are fundamentally within that file's
package. When they are accessed from outside of that package, they have to be
qualified with the package name. From inside the package, however, names from
that package can be used without qualification, as a matter of convenience.
Conceptually, we consider all code within one package to belong together.

## Imports

Carbon files access interfaces declared in other files by importing them. All
imports for a file must be immediately after the package declaration and in a
contiguous block. No other code can be interleaved with the imports. There are
also three forms of import based on the distance between the files:
jonmeow marked this conversation as resolved.
Show resolved Hide resolved

```
package Abseil library Time;

// Importing a library from some other package.
import Widget library Wombat;
jonmeow marked this conversation as resolved.
Show resolved Hide resolved

// Importing another library from the same package.
import library Container;

// Importing another file from the same library.
import file "internal/Duration.cb";

...
```

Two of these imports, the one from a file and the one from another library in
jonmeow marked this conversation as resolved.
Show resolved Hide resolved
the same package, are fundamentally importing _local_ code -- all of this code
resides in a single package. As such, they directly import names into the
package's namespace and those names are visible with unqualified name lookup.

**Open question:** The importing of a file using a quoted string to name it is an interesting but potentially risky approach. We suggested similar techniques for C++ modules and so have done some work to understand the implications and it at least looks reasonable to implement and use. But it may be too surprising for programmers, so this may be an area we revisit. Certainly, the C++ committee was quite unhappy with taking advantage of the fact that some of the organization and naming has already been done with filenames, requiring "module partitions" which have a custom name syntax.

The more general form of import is importing external code into the package. As
a consequence, the only name that becomes visible is the name of that package,
and everything else is found under it using the standard namespace dotted
traversal (`Widget.Stuff`).
jonmeow marked this conversation as resolved.
Show resolved Hide resolved

Each import brings in a specific library (or file), not the entire package. When
jonmeow marked this conversation as resolved.
Show resolved Hide resolved
importing an unnamed library from a package (which must be the only library in
the package), these are indistinguishable and so the library component can be
jonmeow marked this conversation as resolved.
Show resolved Hide resolved
simply omitted:

```
import SimplePackage;
```

This imports the entire package because that consists of a single library.

A single import can name multiple libraries within the package to import:

```
import Abseil libraries: Container, Time;
```

**Open question:** We might investigate some different syntax alternatives for multi-import. Automatic formatting should be able to make the above at least somewhat cleaner for long lists:

```
import Abseil libraries:
ModuleNumber1,
ModuleNumber2,
ModuleNumber3,
ModuleNumber4,
ModuleNumber5,
ModuleNumber6,
ModuleNumber7,
ModuleNumber8,
ModuleNumber9,
ModuleNumber10;
```

But maybe people would prefer other more structured syntaxes like `(a, b, c, ...)`. We can investigate these and see what sticks.

When importing a named package, the imported name must exactly match that
jonmeow marked this conversation as resolved.
Show resolved Hide resolved
package's declared package name -- no renaming is permitted. Aliases plus a
wrapping package can be used to incrementally migrate uses of a package to a new
name, then to a wrapping package using the new name, and finally remove the old
package, but this will remain a non-trivial refactoring. The reason this is
important is that the name of the package essentially forms the identity of
every exported interface component and in the face of templates must be stable
for the purpose of linking together code.

**Future work:** Carbon should also support importing packages from a URL that identifies the repository where that package can be found. This can be used to help drive package management tooling and to support providing a non-name identity for a package that is used to enable handling conflicted package names.

## Shadowing of names

Carbon completely forbids shadowing of names. Because there is no global
namespace and no using directive, unqualified names are entirely within local
control and so should not require shadowing for maintenance or development.
Allowing shadowing has a long history of bugs and confusion. While it is used in
some clear and readable places in C++, those situations are likely better
addressed with language facilities than cleverly shadowed names.

**Open question:** This is an extremely restrictive stance. It has many advantages, but may end up being too costly. We can easily relax it if needed, potentially with special syntax to handle specific edge cases or as an interim measure to aid migration. Or if data emerges, we can simply revisit it entirely.

## Standard library names

When discussing standard library names, we mean the core of the standard library
that is generically useful and applicable to code and application in nearly
every domain, such as the STL in C++. More domain-specific libraries, while we
jonmeow marked this conversation as resolved.
Show resolved Hide resolved
may choose to standardize them for Carbon, don't need any special consideration
and should be organized into domain-relevant packages like any other libraries.
We also don't think this core part of the standard library would meaningfully be
represented as a single Carbon library -- Carbon's libraries are more fine
grained.

The package named "Carbon" will provide this core part of the standard "library"
as a collection of Carbon libraries providing similar facilities to what is
found in the C++ STL. Other than the package name, it would work just like any
other package of libraries.

However, there are some specific wrinkles with some things provided by this
package that motivate some special facilities.

The first interesting aspect is that we would like to make even fundamental and
primitive types in Carbon use interfaces that are no more special than user
defined types. This means `Int32` should be a "normal" type rather than a
keyword, and be provided by the standard library.

This raises the second interesting aspect: it doesn't seem reasonable for Carbon
code to need to import some library in order to access `Int` or `Bool` types,
despite wanting those interfaces to be defined using normal Carbon syntax and
facilities. Further, they would be imported as the `Carbon` package requiring
them to be qualified with a package name: `Carbon.Int`, etc. This seems
significantly too onerous.

To address these issues, all Carbon files have an implicit import of the
`Fundamentals` library from the `Carbon` package as if the file contained the
line:

```
import Carbon library Fundamentals;
```

Further, all Carbon files have an implicit alias of every exported name in this
library into their file's scope.

**Open question:** We could add an automatic syntax for creating aliases while importing, and reduce this to a single implicit import using that facility:

```
import Carbon library Fundamentals with aliases: Int32, Int64, Bool, ...;
```

It isn't clear whether this is an important convenience to expose outside of the implicit case so we've simply used an implicit alias for now, but we can revisit this if needed.

This makes the `Fundamentals` library extremely special in one final way: adding
new exported names to this library is a potentially globally breaking change and
would require shipping some update tooling to users. Because of the lack of
shadowing, every name in this library is nearly as disruptive as a keyword.
Fortunately, we expect this library to be both fairly small and extremely
unlikely to change. The kinds of names exported from it often _are_ keywords in
other languages (including C++).

However, this does mean that a collection of standard library names (those from
the `Fundamentals` library) will be used unqualified in essentially all cases in
Carbon code. For consistency, we should recommend that names from standard
libraries in the Carbon package are typically aliased and used unqualified for
consistency. Skipping this and using the qualified form is always available to
work around collisions. The fact that the package name isn't extra short may
help incentivise consistent use of aliases here. Because each aliased name is
explicitly introduced in the code, these don't cause any evolution problems for
Carbon libraries -- newly added names won't be implicitly aliased for any
library outside of `Fundamentals`.

**Open question:** If we use an embedded alias-in-the-import syntax, we could additionally require using that for any imports of standard libraries, and make the `Carbon` package an unnamed and unnamable package where every usable name must be aliased into the importer's scope. This would ensure consistency, remove the visible name `Carbon` but would make it more complicated to handle name collisions and add yet more special syntax usage.