Repository: go
Issue: proposal: spec: tuples as sugar for structs
Labels: LanguageChange,Proposal,LanguageChangeReview

Updates:
-  
only allow `unpack` when all fields are exported

-  `unpack` skips unexported fields of structs from different packages
-  include a vet rule
-  explicitly include a change to Go 1 compat agreement so that `unpack` is treated the same as unkeyed struct literals

This proposal adds basic tuples to Go with one piece of sugar and two builtins.

The sugar is for `struct(T0, T1, …, Tn)` to be shorthand for a struct type with `n` fields where the `i`th field has type `Ti` and is named `fmt.Sprintf("F%d", i)`. For example, `struct(int, string)` is a more compact way to write `struct { F0 int; F1 string }`.

This gives us a tuple type notation for and answers all questions about how they behave (as the desugared struct they are equivalent to). By naming the fields `F0` and so on, this both provides accessors for the individual elements and states that all tuple fields are exported.

The variadic `pack` builtin returns an anonymous tuple, with the appropriate types and values from its arguments. In particular, by ordinary function call rules, this allows conversion of a function with multiple returns into a tuple. So `pack(1, "x")` is equivalent to `struct(int, string){1, "x"}` and, given `func f() (int, error)`, the statement `t := pack(f())` produces the same value for `t` as the below:
```go
n, err := f()
t := struct(int, error){n, err}
```

The `unpack` builtin takes any struct value and returns all of fields in the order of their definition, skipping `_` fields and unexported fields from a different package. (This has to be somewhat more generally defined as tuples aren't a separate concept in the language under this proposal.) This is always the inverse of `pack`. Example:
```go
// in goroutine 1
c <- pack(cmd_repeat, n)

// in goroutine 2
cmd, payload := unpack(<-c)
```

The `struct()` sugar let's us write pairs, triples, and so on for values of mixed types without having to worry about names. The `pack` and `unpack` builtins make it easier to produce and consume these values.

No changes are needed to the public API of `reflect` or `go/types` to handle tuples as they're just structs, though helpers to determine if a given struct is "tuple-y" may be useful. `go/ast` would need a flag in `StructType` noting when a struct used the tuple syntax but as long as the implicit field names are explicitly added by the parser. The only place this would be needed is for converting an AST back to a string.

The only potential danger here is `unpack`. If it's used on a non-tuple struct type from a different package it would be a breaking change for that package to add an additional exported field. Go 1 compat should be updated to say that this is a acceptable just as it says that adding a field that breaks an unkeyed struct literal is acceptable. Additionally, a go vet check should be added that limits `unpack` to structs with exclusively "F0", "F1", …, "Fn" field names. This can be relaxed at a later time.

This is a polished version of an earlier comment of mine: https://github.com/golang/go/issues/33080#issuecomment-612543798 In the years since I've written many one-off types that could have just been tuples and experimented with generics+code generation to fill in the gap. There have been multiple calls for tuples in random threads here and there and a few proposals:
- #61920
- #32941
- #33080 (not tuples per se but related)