In order to support exporting imported names, add
export import library <library> and export <name reference> syntax.
As we develop libraries such as the prelude, we want the ability to indicate
that an imported name should be re-exported for indirect use. At present, we can
use the prototype alias to expose names on a case-by-case basis
(alias Foo = Bar;), but it doesn't work to export the same name
(alias Foo = Foo; is a name conflict), and we want to be able to more broadly
forward a library's exported names.
For example:
package Foo library "internal";
// Declare C.
class C;
package Foo;
// We want the ability to expose everything imported here.
import library "internal";
import library Foo;
// Uses C by way of the default library.
var c: Foo.C;
Some of the syntax options were discussed on Discord.
Names declared in a Carbon file are currently exported by default. A private
keyword may be used to prevent that export. Note C++ and TypeScript use
private-by-default behavior, so the syntax choices that make sense elsewhere may
not make as much sense for Carbon.
As described in the problem statement, alias offers incomplete re-export
support. However, alias is not fully designed; it's modeled on informal
discussions.
In C++ modules, this is
export import ....
In TypeScript modules, similar syntax might look like:
import * from '<module>'
export * from '<module>'
In Python, names in a module are generally public, and imported names are
accessible too. For example, given import datetime, the module makes the name
datetime available to clients. There is interest in
more explicit export syntax.
In other languages, such as Java, Kotlin, or Go, direct re-exports aren't supported. Instead, the expectation seems to be that either a copy of the entity would be exported, or it should just be moved.
Support the export keyword as a modifier to import library <library>
(excluding cross-package imports). This is export import for short. For
example:
export import library "lib";
Additionally, support the export keyword on individual, file-scoped or
namespace-scoped entities (excluding entities in other packages, and namespaces
themselves). This is export name for short. For example:
// Export an entity:
export Foo;
// Export an entity inside a namespace:
export NS.Bar;
// Invalid: exporting namespaces is disallowed.
export NS;
Although exporting cross-package names is disallowed, note that alias can be
used to add a package-local name that originates from another package, which
then is valid for export. For example:
import package Other;
// Invalid: cross-package exports are disallowed.
export Other.Obj;
// This introduces a package-local name. The alias is exported, and other
// libraries importing this library may export `Obj`.
alias Obj = Other.Obj;
The export keyword is only valid in files which are valid to import. It is
invalid in files which cannot be imported: implementation files and
Main//default.
In the
source file introduction,
export import directives are intermingled with other import directives.
export name directives are normal code and cannot be intermingled with any
import directives, including export import directives.
This allows:
import library "foo";
export import library "wiz";
import library "bar";
export FooType;
class C { ... };
export BarType;
This disallows:
import library "foo";
// Invalid: All `import` directives must come before other code, including
// `export name`.
export FooType;
import library "bar";
class C { ... };
// Invalid: `export import` must be grouped with `import` directives.
export import library "wiz";
Namespaces are not valid arguments to export; entities in namespaces must be
individually exported.
This keeps open a future design option of having export on a namespace export
all imported names inside the namespace, such as export NS;. This could also
be achieved with * syntax, such as export NS.*;. There hasn't been
discussion of this option, and this proposal takes no stance on the option.
export <name> allows moving entities between libraries without needing
to make modifications to clients, enabling more incremental
refactorings.prelude.carbon that exports all related functionality.export syntax structuresWe discussed several different syntax choices.
A couple placement alternatives discussed were:
export before library. For example, import export library "lib".
import Foo export library "lib" could make it clearer the library is
being re-exported, rather than the package.export as a suffix. For example, import library "lib" export.
import statements line up better
when some may not have the export modifier.The current design uses export as a prefix. This is for consistency with how
we put other modifier keywords, such as private or extern, prior to the
introducer keyword.
A couple keyword alternatives discussed (alongside placement options) were:
reexport
export because it mirrors import, and it's
consistent with multiple other languages. It's also shorter, and Carbon
often chooses keywords for shortness.exported and reexported
export or reexport.export name placementsWe see several options for export name placement. This compares them, focusing
on advantages and disadvantages for each option.
export name with imports
export name can (only) appear in the preamble, with the imports, and
cannot appear with the other declarations in the library. Note this option
could either have export name refer to earlier imports (creating an
ordering consistency issue), or expend additional effort in order to track
whether a name was already imported at the site of the export.
Advantages:
export locally
introduced names.Disadvantages:
export name with other declarations
export name can only appear after imports. This means that all names valid
for export will already be made available.
Advantages:
import remains very special.export are already imported.Disadvantages:
export name next to the import that is expected to add
the name.export import and export name will be in different sections: no
single place to look for re-exports.No ordering for export name
Let developers choose what the prefer.
Advantages:
Disadvantages:
import as special.We're choosing option (2). The name lookup issues avoided by requiring export
be below import directives seem worthwhile.
A possible option to (2) might be to create an additional section dedicated to
export name below the import section. This proposal suggests avoiding that
in order to avoid increasing the amount of enforced ordering in Carbon files.
As proposed, re-exporting names from other packages will not be supported. This is done to continue maintaining package boundaries, and so that names aren't unexpectedly introduced. For example:
package Foo;
class C;
package Bar;
export import Foo;
package Wiz;
import Bar;
In the last package Wiz, it might be confusing if the name Foo.C should be
introduced: typically importing Bar would put everything under the Bar
namespace.
We may choose to re-examine this choice, but this proposal does not include support.