A few questions (I'm ignoring generics for now, and have not cross-referenced the other proposals).
1. Is it possible to access the n'th element of an implicitly typed tuple? More generally, how do you unpack such a tuple - other than by assigning to a compatible named tuple type? 2. How does it interact with multiple assignment? Is the following valid, with or without "..." notation? a, b := (1, 2) 3. Are implicit tuples allowed on the LHS? Are either of the following valid? (a, b) := (1, 2) (a, b) := 1, 2 4. How does it interact with functions returning multiple values? If I have a function which returns (string, error) then can I write t := myfunc() and is t implicitly a tuple of (string, error) ? Also with explicit types, e.g. var t (msg string, err error) t = myfunc() More generally, does there remain a difference between a function returning multiple values, and a function returning a tuple? 5. Given that you write type Point (X, Y int) does this mean that "(X, Y int)" is valid wherever a type is normally specified, e.g. var a (X, Y int) This could lead to ambiguity with functions: function foo() (X, Y int) { ... } Currently that's a function returning two named values (and "X" and "Y" are available for assignment within the functino body); but under your scheme, "(X, Y int)" is also a single type literal. I guess you could resolve that ambiguity by requiring: function foo() ((X, Y int)) { ... } // function returns a tuple 6. Is a one-element tuple allowed? e.g. t := (0,) t := (X int)(0) 7. Do untyped constants work as per normal assignment? I would expect: t1 := (0, 0) // OK, implicit type is (int, int) int32 a, b t2 := (a, b) // implicit type is (int32, int32) type Pair64 (X, Y int64) var t3, t4, t5 Pair64 t3 = (0, 0) // allowed: an untyped constant can be assigned to int64 t4 = t1 // not allowed, t1 is implicitly (int, int) and int cannot be assigned to int64 without conversion t5 = t2 // not allowed, cannot assign int32 to int64 // Casting?? x := Pair64((0, 0)) y := Pair64(t1) z := (X, Y int64)((0, 0)) 8. Observation: you now have two very similar but different concepts, tuples and structs: type Point1 (X, Y int) type Point2 struct{X, Y int} The alternative ISTM would be to have "implicitly typed struct literals", e.g. var a Point2 a = {1, 2} That seems to be a smaller change, and I thought I had seen a proposal like this before, but I haven't dug around to find a reference. Are there use cases that your proposal allows, which this doesn't? On Sunday 28 April 2024 at 12:10:02 UTC+1 Andrew Harris wrote: > Bouncing out from some recent discussions on the github issue tracker, it > seems like there's some interest in tuples in Go. I thought the discussion > in #66651 led to some interesting ideas, but it's also beginning to drift. > Maybe this is a better place to brain-dump some ideas. (This could be a > proposal but I'm not sure that's quite right either, that might be spammy.) > > Some recent issues: > 1. #64457 "Tuple types for Go" <https://github.com/golang/go/issues/64457> > (@griesemer) > 2. #66651 "Variadic type parameters" > <https://github.com/golang/go/issues/66651> (@ianlancetaylor) > 3. "support for easy packing/unpacking of struct types" > <https://github.com/golang/go/issues/64613> (@griesemer) > > Synthesizing from those discussions, and satisfying requirements framed > by @rogpeppe > <https://github.com/golang/go/issues/66651#issuecomment-2054198677>, the > following is a design for tuples that comes in two parts. The first part > explores tuples in non-generic code, resembling a restrained version of > #64457. The second part explores tuple constraints for generic code, > reframing some ideas from #66651 in terms of tuples. It's a fungal kingdom > approach, where tuples occupy some unique niches but aren't intended to > dominate the landscape. > > *TUPLES IN NON-GENERIC CODE* > > Tuples are evil > <https://github.com/golang/go/issues/32941#issuecomment-509367113> because > the naming schemes are deficient. To enjoy greater name abundancy, this > design tweaks tuple *types* from #64457 in the direction of > "super-lightweight > structs" > <https://github.com/golang/go/issues/64457#issuecomment-1834358907>. It > still allows tuple *expressions* from #64457, for tuples constructed from > bare values. > > *1. Tuple types* > Outside of generics, tuple *type* syntax requires named fields. > > TupleType = "(" { IdentifierList Type [ ", " ] } ")" . > > // e.g.: > type Point (X, Y int) > > More irregularly, the TupleType syntax is used *exclusively* to declare > named types, and these named tuple types cannot implement methods. As a > result, a named tuple type is entirely defined at the site of the type > definition. > > *2. Tuple literals* > The tuple *expression* syntax of #64457 remains valid. The result is an > implicitly typed tuple value. Literals of a named tuple type are also > valid, and resemble struct literals. > > point1 := (0, 0) // implicitly typed > point2 := Point(X: 0, Y: 0) // explicitly typed > > > *3. Promotion and expansion* > There is no way to capture the type of an implicitly typed tuple value - > the result of a bare tuple *expression* - with tuple *type* syntax. > However, promotion and expansion are available as way to leverage tuple > values. > > - Promotion: An implicitly typed tuple value is freely and automatically > promoted to a value of a named tuple type, if and only if the sequence of > types is congruent (same types, same order, same arity) between the > implicit and named type: > > type T (string, string) > var t T > t := ("foo", "bar") > > The RHS of the assignment is implicitly typed (string, string), so the > value can be promoted to the LHS's congruent type T without further > ceremony. > > - Any tuple value can, under the condition of congruence, expand with ... > "wherever > a list of values is expected" (#66651). This means places like assignments, > function calls, function returns, struct/slice/array literals, for/range > loops, and channel receives. Each of the github issues (#64457, #64613, > #66651) explores this in more detail. Qualifications and some subjectivity > are involved, and a full proposal would explore this more completely and > sharply, but the intuitive notion is pretty straightforward. > > > *TUPLE CONSTRAINTS* > For generic code, this design's driving concept is tuple constraints. A > tuple constraint describes type sets that are exclusively composed of tuple > types. Loosely, where union-of-types or set-of-methods type constraints are > currently, a tuple constraint would also be allowed. The rules for code > parameterized on tuple constraints should resemble #66651 in many ways. > Most essentially, it should be possible to substitute a tuple constraint > "wherever a list of types is permitted", as suggested in #66651. > > > *1. Non-variadic tuple constraints* > The current TypeParamDecl production is: > > TypeParamDecl = IdentifierList TypeConstraint . > > Adding tuple constraints can be accomplished by extending TypeParamDecl > syntax > to include an alternative to the TypeConstraint, a TupleConstraint. Then, > a tuple constraint is constructed from TypeConstraint elements. > > TypeParamDecl = IdentifierList ( TypeConstraint | TupleConstraint ) . > TupleConstraint = "(" { TypeConstraint [ "," ] } ")" . > > Some examples: > [T (any, any)] describes the type set consisting of any 2-ary tuple > [T (K, any), K comparable] describes the type set of 2-ary tuples that > begin with a comparable element. > > Via tuple -> list-of-types substitution, the following would be equivalent: > > func F[K comparable, V any](f func(K, V)) { ... } > func F[KV (comparable, any)](f func(KV)) { ... } > > *2. Variadic tuple constraints* > > A variadic tuple constraint is described with an extension to the > TupleConstraint production: an optional VariadicTupleElement is appended > to it. > > TupleConstraint = "(" { TypeConstraint [ "," ] } [ VariadicTupleElement ] > ")" . > VariadicTupleElement = "..." TypeConstraint . > > The identifier for a variadic tuple constraint may be still be substituted > for a list of types. Drawing from use cases discussed in #66651, this leads > to function signatures like: > > func Filter[V (... any)](f func(V), seq Seq[V]) Seq[V] > > func MergeFunc[V (... any)](xs, ys Seq[V], f func(V, V) int) Seq[V] > > Additionally, tuple constraints can accommodate multiple variadic type > parameters: > > func Zip[T0 (... any), T1 (... any)](xs Seq[T0], ys Seq[T1]) > Seq[Zipped[T1, T2]] > > func Memoize[In (... comparable), Out (... any)](f func (In) Out) func(In) > Out > > *3. Instantiation and unification* > > Like #66651, variadic type parameters are only instantiated by > non-variadic types. Unification of a concrete tuple type with a tuple > constraint considers the compatibility of tuple and constraint arity, and > compatibility of tuple and constraint elements. > > When unifying type parameters, tracking fixed or minimum arity is > significant. Note that the fixed arity of a non-variadic tuple constraint > and the minimum arity of a variadic tuple constraint is implicit in the > notation. For example: > > [T (any, any)] -> any 2-ary tuple > [T (any, any, any, ... any)] -> any tuple of arity 3 or greater > > The intersection of any two tuple constraints is calculable, composable, > and order independent. (Or, at least the arity question has these > properties, and I believe the per-element question is a well - as I > understand that's an important property of unification currently.) > > *Further questions* > > - The inverse of tuple constraint -> list-of-type substitution, inferring > a tuple constraint from a list of types, seems tractable. Maybe it's even > useful. > - This design doesn't propose ... unpacking for structs, as suggested in > #64613. Is something here helpful? > - This design only allows a single trailing variadic element in a tuple > constraint. Comments on #66651 explored uses that would require a single > leading variadic element. I don't know whether or not this works formally, > but it's intriguing. > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/e2673a9d-fb32-4ff3-9f2c-13da54db3364n%40googlegroups.com.