Brian:

1: There would be no access to implicitly typed tuple elements. Both 
implicit and named tuple values would expand with `...`, for example in an 
assignment like
`x, y := (0, 0)...`. (FWIW, #64457 suggested decimal names for tuple 
elements `.0`, `.1` etc.)

2: #64457 and #66651 revealed some subjective preferences in discussion - 
the `...` seem to be unneccessary, but readable. I lean towards thinking 
ellipsis are useful for any of these proposals.

3: No, no implicit tuples on LHS.

4, 5, 6: The somewhat underhanded trick here would be that the `(int, int)` 
type literal would only be usable when declaring a named type (`type T 
(int, int)`), in part to elide these questions. #64457 I think suggested 
additional parentheses or commas could work to resolve these questions 
otherwise.

7: I think the assignments match what I would expect. Casting I think would 
work so long as untyped constant tuples are valid. They would be necessary 
for something like `var a, b int = (0, 0)...` also.

8: Yeah - this version of tuples is not really orthagonal to structs.

Ian:

Thanks for the feedback. I guess I'm looking at struct splatting from 
#64613, in combination with variadic type parameters from #66615, as 
covering similar use cases as this. There's some difference in formal 
properties. But, I'm convinced the rabbit in the hat is dead for this 
version of tuples.

Roger:

> For me, tuples start to make sense in Go mainly because they map directly 
to and from the tuple-like values we already pass to and from functions, 
and specifically in the context of a variadic type parameter language 
feature that lets us name a set of such values directly.

This resonates - without variadic type parameters I don't think tuples add 
much, but I think they are really intriguing for variadic type parameters.

> For example, it seems to me that a function that zips two iterators 
together into pairs of values would be no clearer if the members of the 
pair were named or not.

An example of where I think confusion might arise would be: a bare (int, 
int) type might be row-major or column-major matrix indices, or on-screen 
coordinates with origin in the top-left corner, or Cartesian coordinates, 
etc. - logically distinct types that map onto the same tuple, structurally. 
In this situation I always reach for named types in other languages.

#64457 allowed defining named types, but isn't strict about them ... that's 
maybe not fatal to the idea of type constraints based on tuple structure, 
but some version of tuples for non-generic code would have to come with it.
On Sunday, April 28, 2024 at 2:24:47 PM UTC-7 roger peppe wrote:

> I agree with Ian that the named field syntax is too close to structs.
>
> For me, tuples start to make sense in Go mainly because they map
> directly to and from the tuple-like values we already
> pass to and from functions, and specifically in the context of a variadic
> type parameter language feature that lets us name a set of such values
> directly.
>
> That is, in my view, if I if I am able to write code that handles a 
> function
> that takes an arbitrary number of parameters, it makes sense to be able to 
> store
> those values in a variable with a regular data type and use that in
> all the ways that one might use any other variable.
>
> To my mind a tuple with unnamed members is the only reasonable choice
> for such a data type because function argument and return parameters
> are themselves unnamed (†), so any other choice quickly leads to 
> tricky-to-solve
> type-compatibility issues.
>
> While tuples can be evil, I don't believe they're *inherently* evil, any 
> more than
> I believe that functions that take more than one argument are inherently 
> evil.
>
> That is, if tuples were added to the language I'd hope that best
> practice guides would make it very clear that tuples are appropriate
> only in situations when the *only* information available about the members
> is their position.
>
> For example, it seems to me that a function that zips two
> iterators together into pairs of values would be no clearer if the
> members of the pair were named or not.
>
> I think this kind of situation occurs most often in generic code,
> which is why perhaps this might be an OK time to add tuples
> to Go.
>
>   cheers,
>     rog.
>
> (†) named parameters in the function body itself do not affect the 
> function's type.
>
> On Sun, 28 Apr 2024 at 12:10, Andrew Harris <harr...@spu.edu> 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...@googlegroups.com.
>> To view this discussion on the web visit 
>> https://groups.google.com/d/msgid/golang-nuts/32547c6c-a9c0-4f9d-8a27-41e9173ba85dn%40googlegroups.com
>>  
>> <https://groups.google.com/d/msgid/golang-nuts/32547c6c-a9c0-4f9d-8a27-41e9173ba85dn%40googlegroups.com?utm_medium=email&utm_source=footer>
>> .
>>
>

-- 
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/86bb90f8-34fc-43e5-ad09-9ea42e813037n%40googlegroups.com.

Reply via email to