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

Create a tuples proposal #2038

Closed
josh11b opened this issue Aug 15, 2022 · 6 comments
Closed

Create a tuples proposal #2038

josh11b opened this issue Aug 15, 2022 · 6 comments
Labels
long term Issues expected to take over 90 days to resolve.

Comments

@josh11b
Copy link
Contributor

josh11b commented Aug 15, 2022

I created the original tuples proposal, #111 . One of the use cases for tuples is to be able to represent argument lists. At the time, I was trying to support named/keyword arguments, and so the original tuple design supported both positional and named members. Since tuples were going to support named members, tuples were going to be used for struct literals / class value initializers. One motivation for trying to support named/keyword arguments earlier rather than later was to support specifying the values of associated types in the generics design, as is done in Rust, see https://rust-lang.github.io/rfcs/0195-associated-items.html#constraining-associated-types . Once we decided to use the where syntax for this in issue #780 , the named/keyword arguments issue was deferred, see #478 and #505 . At that point, tuples in (...) were made only positional and {...} was determined to be better for the named member case, for struct literals, class initializers, and possibly eventually for named/keyword arguments, see #561 .

So that leaves tuples using (...) with positional members only, to address these use cases:

  • They are a light-weight product type.
  • They support multiple return values from functions.
  • They are a literal that can be used to initialize an array, as long as all members can be implicitly converted to the array's type.
  • A tuple may be unpacked into multiple arguments of a function call.
  • They may be involved in pattern matching use cases: destructuring functions returning tuples for multiple return values, and supporting varying numbers of arguments in a function signature (called "variadics") [Note: no pattern matching design has been accepted as of this writing, so all pattern matching design is provisional]

This leaves a few design decisions, but I don't think there will be much push back from the leads from a proposal that matches what we've been using provisionally:

  • Zero-tuples: We have been saying zero-tuples are written (). We have been using them in place of the void type in some cases, but have more often been finding other ways than a void type to address those use cases.
  • One-tuples: We have been saying that all tuples are written in round parens, but round parens are also used for grouping: x * (y + z). So we've been disambiguating by saying (<expression>) without commas is grouping, and a trailing comma is used to make a one-tuple, as in (1,). My preference is that a trailing comma is allowed for other tuples, but see the discussion below.
  • Tuple types: We have been saying that the "type of a tuple is the tuple of the types of its members", so you can write var x: (i32, bool) = (3, true);. This is an experiment that the leads have been interested in running. It means that tuple types are concise and convenient. It also means that () is both the empty tuple and the type of the empty tuple (something we've accepted with structs as well). A consequence of living with this rule is there is nothing about a value that says whether it is a type or not. Some values may be used as type, and some types may be used as values. This is part of the "types are values" approach, and has put restrictions on the rest of the language that we have so far been able to live with: specifically that something is type if it is legal to use it in a type, and it is used in a type position such as the right of a colon :.

Optional trailing comma might lead to some controversy. In general it is good to only have one way to do things, but trailing commas are helpful when formatting one member per line, see the code style of the black formatter for Python.

Note that tuples should probably be considered similar to "data classes" and so should have rules consistent with #710 and #981 , such as supporting < and == comparison as long as all member types do.

Alternative considered: using other symbols than round parens (...) to construct a tuple has been considered. The benefit of these alternatives is that round parens (...) are doing a lot of other things in Carbon including function parameter lists and expression grouping. Expression grouping means that (...) without a comma , can't be a tuple (except ()). This leads to the rule that one-tuples are written with a trailing comma: (<value>,), but this is inconsistent with other tuples as well as calling functions with one argument. Examples of these alternatives can be found in #111 (in particular here), and in discussions such as open discussion on 2022-08-08.

Concern: The subscripting proposal has not been accepted yet, see #1356 . It might make sense to wait for that to be resolved first. Tuples are an interesting case since different members have different types, so the argument to the subscript needs to be a value that can be evaluated at compile time in order to do type checking. The alternative to use .0, .1, and so on was considered in #111.

Extra tuple features that were in previous proposals, like slicing or multiple indices, would probably be best left for a follow-on proposal, since almost none of our uses of tuples have really needed them. Much better to get a proposal in that covers the basics we actually need than block that on the debate and time needed to solidify those extra features.

@tkoeppe
Copy link
Contributor

tkoeppe commented Aug 15, 2022

I posted another alternative on Discord which I think wasn't recorded anywhere else: use a more complex opening syntax. For example, using @ as a placeholder to be determined, the opening syntax could be (@, so that an empty tuple would be (@), and then (@, a), (@, a, b), (@, a, b, c), etc. I.e. you'd always have precisely as many commas as tuple elements.

@OlaFosheimGrostad
Copy link

OlaFosheimGrostad commented Aug 16, 2022

I think trailing comma is an ok tradeoff. Several languages (C++, Dart, TypeScript, Python,…) allow trailing commas for list literals as it makes it easier to add/remove elements without getting syntax errors, so it has some usability advantage for longer literals (once you get used to it).

@L4stR1t3s
Copy link

I don't like the idea of using trailing commas. It looks like something is missing. It would also mean that code may sometimes compile when something is actually missing.

I also don't agree that trailing commas in C++ are allowed to make it easier to add/remove entries. I think trailing commas in C++ are allowed to be able to do this:

enum 
{
#ifdef ONE
	One,
#endif
#ifdef TWO
	Two
#endif
}

If ONE is defined, but not TWO, you would get a compile error if trailing commas weren't allowed. It would still be possible to achieve the same, but the code would be more complicated (and would grow increasingly complicated depending on the amount of defines).

@zygoloid
Copy link
Contributor

Note the question of one-tuple syntax was factored out into #2191 and has been decided by the leads: we will allow trailing commas in all non-empty parenthesized comma-separated lists.

@github-actions
Copy link

We triage inactive PRs and issues in order to make it easier to find active work. If this issue should remain active or becomes active again, please comment or remove the inactive label. The long term label can also be added for issues which are expected to take time.
This issue is labeled inactive because the last activity was over 90 days ago.

@github-actions github-actions bot added the inactive Issues and PRs which have been inactive for at least 90 days. label Dec 20, 2022
@chandlerc chandlerc added long term Issues expected to take over 90 days to resolve. and removed inactive Issues and PRs which have been inactive for at least 90 days. labels Dec 21, 2022
@geoffromer
Copy link
Contributor

I believe this was resolved by #3646.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
long term Issues expected to take over 90 days to resolve.
Projects
None yet
Development

No branches or pull requests

7 participants