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.

Reply via email to