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 <harris...@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+unsubscr...@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/CAJhgachfeoFDajX7D6fC6_ORvU_ySey%2BafJ0c-S_8H1_A9eFvQ%40mail.gmail.com.

Reply via email to