Co-authored by: chandlerc - Based on [PR 22](https://github.com/carbon-language/carbon-lang/pull/83) - [Idea topic](https://forums.carbon-lang.dev/t/proposal-for-an-incomplete-rough-high-level-overview-ready-for-early-feedback/52) - [RFC](https://forums.carbon-lang.dev/t/rfc-an-incomplete-early-and-in-progress-overview-of-the-language-design/73) - [Decision announcement](https://forums.carbon-lang.dev/t/accepted-an-incomplete-early-and-in-progress-overview-of-the-language-design/110) This proposal should be considered a starting point of the language design. It's not intended to be final; language details may change. This is intended to offer a reasonable starting point for: - Example code. - Conceptualizing Carbon at a high level. - Reasonable, but not necessarily final, approaches to features in README.md. - If any idea is obviously bad, we can clean it up here. This proposal is not intended to achieve: - A whole language design. - This is way too much work for a single proposal; this is a skeletal framework only. - As we work on feature-specific designs, we may decide to use other approaches. That's fine: we only need somewhere to start. - The summaries in README.md may be expected to change over time. - Feature-specific files aren't intended to be well-written or comprehensive. They are a quick jot of prior thoughts. - We want to avoid getting stuck on language details that we should consider more carefully regardless. If you're passionate about a feature, please feel free to start a new proposal for it. - Each and every aspect of the suggested overview should be subject to careful examination and justification before it becomes a settled plan of record. Chandler started this with https://github.com/carbon-language/carbon-lang/pull/22. I've taken it over with the following changes: - More of a directory hierarchy. - Trying to thin out the main file (now README.md) to lighter summaries of features. - Details/rationale/alternatives should be in feature-specific files. - Draft files are linked as references where added. For an example of how we may proceed with feature-specific designs, see https://github.com/carbon-language/carbon-lang/pull/80. In this structure: - docs/design/README.md mentions interoperability, with a light overview. - The light overview is not yet in https://github.com/carbon-language/carbon-lang/pull/80. - docs/design/interoperability/README.md goes into more depth on interoperability, covering key points of the approach. - Individual files in docs/design/interoperability/* go into more depth on interoperability. Simple designs may not have a subdirectory. All current feature-specific designs do not -- they may be moved later.
4.4 KiB
Tuples
Table of contents
TODO
This is a skeletal design, added to support the overview. It should not be treated as accepted by the core team; rather, it is a placeholder until we have more time to examine this detail. Please feel welcome to rewrite and update as appropriate.
Overview
The primary composite type involves simple aggregation of other types as a tuple (called a "product type" in formal type theory):
fn DoubleBoth(Int: x, Int: y) -> (Int, Int) {
return (2 * x, 2 * y);
}
This function returns a tuple of two integers represented by the type
(Int, Int). The expression to return it uses a special tuple syntax to build a
tuple within an expression: (<expression>, <expression>). This is actually the
same syntax in both cases. The return type is a tuple expression, and the first
and second elements are expressions referring to the Int type. The only
difference is the type of these expressions. Both are tuples, but one is a tuple
of types.
Element access uses subscript syntax:
fn Bar(Int: x, Int: y) -> Int {
var (Int, Int): t = (x, y);
return t[0] + t[1];
}
Tuples also support multiple indices and slicing to restructure tuple elements:
fn Baz(Int: x, Int: y, Int: z) -> (Int, Int) {
var (Int, Int, Int): t1 = (x, y, z);
var (Int, Int, Int): t2 = t1[(2, 1, 0)];
return t2[0 .. 2];
}
This code first reverses the tuple, and then extracts a slice using a half-open range of indices.
Indices as compile-time constants
In the example t1[(2, 1, 0)], we will likely want to restrict these indices to
compile-time constants. Without that, run-time indexing would need to suddenly
switch to a variant-style return type to handle heterogeneous tuples. This would
both be surprising and complex for little or no value.
Open questions
Slicing ranges
The intent of 0 .. 2 is to be syntax for forming a sequence of indices based
on the half-open range [0, 2). There are a bunch of questions we'll need to
answer here:
- Is this valid anywhere? Only some places?
- What is the sequence?
- If it is a tuple of indices, maybe that solves the above issue, and unlike function call indexing with multiple indices is different from indexing with a tuple of indexes.
- Do we need syntax for a closed range (
...perhaps, unclear if that ends up aligned or in conflict with other likely uses of...in pattern matching)? - All of these syntaxes are also very close to
0.2, is that similarity of syntax OK?- Do we want to require the
..to be surrounded by whitespace to minimize that collision?
- Do we want to require the
Single-value tuples
This remains an area of active investigation. There are serious problems with
all approaches here. Without the collapse of one-tuples to scalars we need to
distinguish between a parenthesized expression ((42)) and a one tuple (in
Python or Rust, (42,)), and if we distinguish them then we cannot model a
function call as simply a function name followed by a tuple of arguments; one of
f(0) and f(0,) becomes a special case. With the collapse, we either break
genericity by forbidding (42)[0] from working, or it isn't clear what it means
to access a nested tuple's first element from a parenthesized expression:
((1, 2))[0].
Function pattern match
There are some interesting corner cases we need to expand on to fully and more precisely talk about the exact semantic model of function calls and their pattern match here, especially to handle variadic patterns and forwarding of tuples as arguments. We are hoping for a purely type system answer here without needing templates to be directly involved outside the type system as happens in C++ variadics.
Type vs tuple of types
Is (Int, Int) a type, a tuple of types, or is there even a difference between
the two? Is different syntax needed for these cases?