- What is Carbon?
- What is Carbon's status?
- Why build Carbon?
- What alternatives did you consider? Why did they not work?
- How were specific feature designs chosen?
- How will Carbon work?
- How will the Carbon project work?
- How can I contribute to Carbon?
The Carbon Language is an experimental successor to C++. It is an effort to explore a possible future direction for the C++ language given the difficulties improving C++.
Carbon is still an experiment. There remain significant open questions that we need to answer before the project can consider becoming a production effort. For now, we're focused on exploring this direction and gaining information to begin answering these questions.
Carbon is still years away — even if the experiment succeeds, it's unlikely that it will be ready for serious or production use in the next few years. Everything here is part of a long-term investigation.
One of the critical questions we need to answer as part of this experiment is whether the direction we're exploring with Carbon has both broad and significant interest for the industry at large. We feel like this is best answered by developing the language openly, publicly, and with broad participation.
We've resolved several of the most challenging language design technical decisions we anticipated based on experience with C++ and its constraints, particularly around generics and inheritance. Beyond those two areas, we have initial designs for class types, inheritance, operator overloading, syntactic and lexical structure, and modular code organization. We are aiming to complete the initial 0.1 language design around the end of 2022 although there are a large number of variables in that timeline. See our roadmap for details.
References:
Prior to going public, Carbon has had a couple dozen people involved. GitHub Insights provides activity metrics.
Yes! A prototype interpreter demo explorer
can be used to execute simple
examples. For example:
$ bazel run //explorer -- ./explorer/testdata/print/format_only.carbon
Example source files can be found under /explorer/testdata.
Carbon can also be explored interactively on https://carbon.compiler-explorer.com.
Please ask questions and hold discussions either by using GitHub Discussions or #language-questions on Discord.
GitHub Issues should be reserved for missing features, bugs, and anything else that is fixable by way of a Pull Request.
Establishing a Carbon Language logo isn't a priority right now. Remember that this project is an experiment, and so we think it's best to concentrate efforts on ensuring that the language succeeds at its goals instead.
We have a few drafts in the works, but it requires a fair amount of work to get right, and getting it wrong is costly, so we won't be adding one in the near future. Don't suggest logos, because we need to be careful about how we create one.
See the project README for an overview of the motivation for Carbon. This section dives into specific questions in that space.
Performance is critical for many users today. A few reasons are:
- Cost savings: Organizations with large-scale compute needs care about software performance because it reduces hardware needs.
- Reliable latency: Environments with specific latency needs or concerns with bounding tail latency need to be able to control and improve their latency.
- Resource constraints: Many systems have constrained CPU or memory resources that require precise control over resource usage and performance.
Carbon code will be able to call C++, and the other way around, without overhead. You will be able to migrate a single library to Carbon within a C++ application, or write new Carbon on top of your existing C++ investment.
While Carbon's interoperability may not cover every last case, most C++ style guides (such as the C++ Core Guidelines or Google C++ Style Guide) steer developers away from complex C++ code that's more likely to cause issues, and we expect the vast majority of code to interoperate well.
For example, considering a pure C++ application:
It's possible to migrate a single function to Carbon:
References:
Migration support is a key long-term goal for Carbon.
If a migration occurs, we anticipate:
- Migration tools that automatically translate C++ libraries to Carbon at the file or library level with minimal human assistance.
- Bidirectional C++ interoperability that allows teams to migrate libraries in any order they choose without performance concerns or maintaining interoperability wrappers.
- Test-driven verification that migrations are correct.
A lot of effort has been invested into improving C++, but C++ is difficult to improve.
For example, although P2137 was not accepted, it formed the basis for Carbon's goals.
While we would like to see C++ improve, we don't think that forking C++ is the right path to achieving that goal. A fork could create confusion about what code works with standard C++. We believe a successor programming language is a better approach because it gives more freedom for Carbon's design while retaining the existing C++ ecosystem investments.
If you want to use Rust, and it is technically and economically viable for your project, you should use Rust. In fact, if you can use Rust or any other established programming language, you should. Carbon is for organizations and projects that heavily depend on C++; for example, projects that have a lot of C++ code or use many third-party C++ libraries.
We believe that Rust is an excellent choice for writing software within the pure Rust ecosystem. Software written in Rust has properties that neither C++ nor Carbon have. When you need to call other languages from Rust, RPCs are a good option. Rust is also good for using APIs implemented in a different language in-process, when the cost of maintaining the FFI boundary is reasonable.
When the foreign language API is large, constantly changes, uses advanced C++ features, or makes architectural choices that are incompatible with safe Rust, maintaining a C++/Rust FFI may not be economically viable today (but it is an area of active research: cxx, autocxx, Crubit).
The Carbon community is looking for a language that existing, large, monolithic C++ codebases can incrementally adopt and have a prospect of migrating away from C++ completely. We would be very happy if Rust could be this language. However, we are not certain that:
- Idiomatic, safe Rust can seamlessly integrate into an existing C++ codebase, similarly to how TypeScript code can be added to a large existing JavaScript codebase.
- Developers can incrementally migrate existing C++ code to Rust, just like they can migrate JavaScript to TypeScript one file at a time, while keeping the project working.
See Carbon's goals for an in-depth discussion of Carbon's vision for C++/Carbon interop and migration.
Large existing C++ codebases almost certainly made architectural choices that are incompatible with safe Rust. Specifically:
- Seamless interop where existing, unmodified C++ APIs are made callable
from safe Rust requires the C++ code to follow borrow checking rules at
the API boundary.
- To reduce the amount of Rust-side compile-time checking that makes interop difficult, C++ APIs can be exposed to Rust with pointers instead of references. However, that forces users to write unsafe Rust, which can be even more tricky to write than C++ because it has new kinds of UB compared to C++; for example, stacked borrows violations.
- Seamless interop where safe Rust APIs are made callable from C++ requires C++ users to follow Rust borrow checking rules.
- Incremental migration of C++ to safe Rust means that C++ code gets
converted to Rust without major changes to the architecture, data
structures, or APIs. However Rust imposes stricter rules than C++,
disallowing some design choices that were valid in C++. Therefore, the
original C++ code must follow Rust rules before attempting a conversion.
- Original C++ code must be structured in such a way that the resulting Rust code passes borrow checking. C++ APIs and data structures are not designed with this in mind.
- Migrating C++ to unsafe Rust would still require the code to follow Rust's reference exclusivity and stacked borrows rules.
If you can use one of these languages, you absolutely should.
Garbage collection provides dramatically simpler memory management for developers, but at the expense of performance. The performance cost can range from direct runtime overhead to significant complexity and loss of control over performance. This trade-off makes sense for many applications, and we actively encourage using these languages in those cases. However, we need a solution for C++ use-cases that require its full performance, low-level control, and access to hardware.
Throughout the design, we include 'Alternatives considered' and 'References' sections which can be used to research the decision process for a particular design.
One of our goals for Carbon
is that it should support parsing without contextual or semantic information,
and experience with C++ has shown that using <
as both a binary operator and
an opening delimiter makes that goal difficult to achieve.
For example, in C++, the expression a<b>(c)
could parse as either a function
call with a template argument b
and an ordinary argument c
, or as a chained
comparison (a < b) > (c)
. In order to resolve the ambiguity, the compiler has
to perform name lookup on a
to determine whether there's a function named a
in scope.
It's also worth noting that Carbon doesn't use any kind of brackets to mark template- or checked-generic parameters, so if Carbon had angle brackets, they would mean something different than they do in C++, which could cause confusion. We do use square brackets to mark deduced parameters, as in:
fn Sort[T:! Comparable](a: Vector(T)*)
But deduced parameters aren't the same thing as template parameters. In particular, deduced parameters are never mentioned at the callsite, so those square brackets are never part of the expression syntax.
See Proposal #676: :!
generic syntax for more
background on how and why we chose our current compile-time parameter syntax.
In Carbon, a declaration of a single variable looks like this:
var the_answer: i32 = 42;
But this is just the most common case. The syntax between var
and =
can be
any irrefutable pattern, not just a single
variable binding. For example:
var ((x: i32, _: i32), y: auto) = ((1, 2), (3, 4));
This code is valid, and initializes x
to 1
and y
to (3, 4)
. In the
future, we will probably also support destructuring structs in a similar way,
and many other kinds of patterns are possible.
Now consider how that example would look if the var
token were not required:
((x: i32, _: i32), y: auto) = ((1, 2), (3, 4));
With this example, the parser would need to look four tokens ahead to determine that it's parsing a variable declaration rather than an expression. With more deeply-nested patterns, it would have to look ahead farther. Avoiding this sort of unbounded lookahead is an important part of our fast and scalable development goal.
As discussed above, Carbon variable declarations are actually doing a form of pattern matching. In a declaration like this:
var the_answer: i32 = 42;
the_answer: i32
is an example of a binding pattern, which matches any value
of the appropriate type, and binds the given name to it. The : i32
can't be
omitted, because the_answer
on its own is an expression, and any Carbon
expression is also a valid pattern, which matches if the value being matched is
equal to the value of the expression. So var the_answer = 42;
would try to
match 42
with the value of the expression the_answer
, which requires a
variable named the_answer
to already exist.
There are other ways of approaching pattern matching, but there are tradeoffs. Pattern matching is still on a provisional design, and as of August 2022 it hasn't been fully reviewed with alternatives considered. A future proposal for pattern matching will need to weigh the tradeoffs in more detail, and may come to a different decision.
Carbon is being built using LLVM, and is expected to have Clang dependencies for interoperability.
The Carbon toolchain will compile both Carbon and C++ code together, in order to make the interoperability seamless.
For example, for import Cpp library "<vector>"
, Carbon will:
- Call into Clang to load the AST of the
vector
header file. - Analyze the AST for public APIs, which will be turned into names that can be
accessed from Carbon; for example,
std::vector
isCpp.std.vector
in Carbon. - Use Clang to instantiate the
Cpp.std.vector
template when parameterized references occur in Carbon code.- In other words, C++ templates will be instantiated using standard C++ mechanisms, and the instantiated versions are called by Carbon code.
Some code, such as #define
preprocessor macros, will not work as well. C++
allows arbitrary content in a #define
, and that can be difficult to translate.
As a consequence, this is likely to be a limitation of interoperability and left
to migration.
Carbon's generic programming support will handle both templates (matching C++) and checked generics (common in other languages: Rust, Swift, Go, Kotlin, Java, and so on).
The key difference between the two is that template arguments can only finish type-checking during instantiation, whereas checked generics specify an interface with which arguments can finish type-checking without instantiation. This has a couple of important benefits:
- Type-checking errors for checked generics happen earlier, making it easier for the compiler to produce helpful diagnostics.
- Checked-generic functions can generate less compiled output, allowing
compilation with many uses to be faster.
- For comparison, template instantiations are a major factor for C++ compilation latency.
Although Carbon will prefer checked generics over templates, templates are provided for migration of C++ code.
References:
- Generics: Goals: Better compiler experience
- Generics: Terminology: Checked versus template parameters
Carbon will match C++'s memory model closely in order to maintain zero-overhead interoperability. There may be some changes made as part of supporting memory safety, but performance and interoperability will constrain flexibility in this space.
See memory safety in the project README.
References:
Carbon will provide tooling to assist upgrades of code in response to language
syntax changes, similar to
C++ to Carbon migration tooling.
For example, if a new keyword except
is added in Carbon 1.1, an upgrade tool
might be provided that would accept Carbon 1.0 code and replace except
identifier uses with r#except
raw identifiers
(like Rust provides),
automatically fixing the conflict.
While Carbon remains in early development, upgrade tooling is not ready. It is instead a consideration for declaring Carbon ready for use.
This upgrade approach stands in comparison to enforcing backwards or forwards compatibility.
Carbon is using GitHub for its repository and code reviews. Most non-review discussion occurs on our Discord server.
If you're interested in contributing, you can find more information in our Contributing file.
Any interested developer may propose and discuss changes to Carbon. The Carbon leads are responsible for reviewing proposals and surrounding discussion, then making decisions based on the discussion. As Carbon grows, we expect to add feature teams to distribute responsibility.
The intent of this setup is that Carbon remains a community-driven project, avoiding situations where any single organization controls Carbon's direction.
References:
Carbon's evolution process is iterative: when we make poor decisions, we'll work to fix them. If we realize a mistake quickly, it may make sense to just roll back the decision. Otherwise, a fix will need to follow the normal evolution process, with a proposal explaining why the decision was wrong and proposing a better path forward.
Carbon is under the Apache License v2.0 with LLVM Exceptions. We want Carbon to be available under a permissive open source license. As a programming language with compiler and runtime library considerations, our project has the same core needs as the LLVM project for its license and we build on their work to address these by combining the Apache License with the LLVM Exceptions.
We believe it is important for a programming language like Carbon, if it is successful, to be developed by and for a broad community. We feel that the open source model is the most effective and successful approach for doing this. We're closely modeled on LLVM and other similar open source projects, and want to follow their good examples. We've structured the project to be attractive for industry players big and small to participate in, but also to be resilient and independent long-term.
The open source model, particularly as followed by Apache and LLVM, also provides a strong foundation for handling hard problems like intellectual property and licensing with a broad and diverse group of contributors.
Carbon uses a CLA (Contributor License Agreement) in case we need to fix issues with the license structure in the future, something which has proven to be important in other projects.
Any changes to the license of Carbon would be made very carefully and subject to the exact same decision making process as any other change to the overall project direction.
Initially, Carbon is bootstrapping using Google's CLA. We are planning to create an open source foundation and transfer all Carbon-related rights to it; our goal is for the foundation setup to be similar to other open source projects, such as LLVM or Kubernetes.
Carbon is currently bootstrapping infrastructure with the help of Google. As soon as a foundation is ready to oversee infrastructure, such as continuous integration and the CLA, we plan to transfer them so they are run by the community.
There are many ways to contribute, and we appreciate all of them. Begin by reading the project's Contributing page to learn more about how you can contribute.
Carbon Language isn't ready for use. This section is for people wishing to participate in designing and implementing the language.
Carbon is being designed to migrate C++ codebases and to look familiar to C++ programmers. As such, familiarity with C++ is very important. Carbon is also trying to learn from other programming languages, so having broad experience with other programming languages will be helpful to see tradeoffs in design decisions.
The Carbon toolchain is being implemented in C++, and we also use Python and Starlark. As we're building off of the LLVM project, familiarity with Clang and other parts of LLVM will be advantageous, but not required.
Our contribution tools page documents specific tools we use when building.
Once a decision is made through the evolution process the community should treat it as firmly decided. This doesn't mean that the decision is definitely right or set in stone, it just means we'd like the community to focus time and energy on other issues in the name of progress.
Sometimes, it will be appropriate to revisit a decision; for example, when there is new information introduced, a new community joins Carbon, or there is an order of magnitude growth in the community. These cases are handled as new proposals through the evolution process.
For example, we have done this with digit separators: we missed important domain conventions and had overly restricted where separators are allowed, an issue was filed with the new information, and we're fixing the choice.
See also the related questions What happens when a decision was wrong?, How does Carbon make decisions?, and What can I do if I disagree with a design decision?.
We invite you to give us constructive feedback. Some of Carbon's design decisions are made with the expectation of receiving community feedback. We understand that many decisions won't be universally popular, but we'd still like to understand the community's reaction to Carbon.
We encourage you to investigate why Carbon came to be the way it is. Designs will include links to the proposals and important alternatives considered that led to them, typically linked at the bottom. Read through and understand the context and rationale behind the decisions you are concerned about. You may find that your concerns were already thoroughly discussed. If not, you will be in a better place to present your thoughts in a convincing way.
Changing decisions that have come out of the evolution process involves a formal process. See When do we revisit decisions or reopen discussions?. For these issues in particular, please be aware that other community members may choose to not actively engage in detailed discussions, especially if the discussion seems to be revisiting points made in the past.
If after reading this answer you are not sure how to proceed please feel free to ask (see Where should I ask questions?).
Both Discord and GitHub Discussions allow you to give an emoji "reaction" to individual posts. If you'd like to amplify what has already been said, please use these instead of posting messages that re-state substantially the same thing. These make conversations easier to follow and understand general sentiment in discussions involving many people.