*> FWIW this kind of restriction has no precedent in Go. Currently any type may be unnamed. This restriction seems arbitrary and irregular to me. * It seems unpopular.
*> Isn't this illegal according to your TupleType syntax above? * Further evidence of a wrong idea :) *>> [T (any, any)] describes the type set consisting of any 2-ary tuple> By analogy with [T map[any]any] I'd expect the "any" there to be the regular "any" type (as used outside generics) rather than the type constraint "any". That is, my intuition would expect this to disallow a (string, string) tuple, but my sense is that that's not what you're suggesting. In general, a type constraint can be either a regular type (a type set) or an interface type, but this doesn't seem like either, and looks to me like it would significantly complicate the generics specification.* Yes - I'm suggesting a third category of constraint. It would be a significant change, and a tradeoff that would add weight to the generics spec. To further detail what I think this involves: 1. The amount of syntax is on the same order as specifying tuples for non-generic code. Introducing tuples is pretty efficient. I'd imagine a sharp, finely tuned framing the tuple->list-of-types substitution adds more cases to existing syntax, on the same order as #64457 (tuples for go) or #66651 (variadic type parameters) would also require. Overall, #64613 (just `...` for structs) seems simpler. 2. On implementation, before considering variadic tuple constraints: There is tuple machinery already in the type system <https://github.com/golang/go/blob/master/src/go/types/tuple.go>, used for assignments and signatures, but tuples are not first class. Also, we know where and how they fit in the unification algorithm <https://github.com/golang/go/blob/16ce8b3925deaeb72541ee96b6ee23a08fc21dea/src/go/types/unify.go#L629>. It's as simple as one could hope: match the arity, then unify the component types. I take this as evidence the idea of a tuple constraint is feasible. 3. On implementation with variadiac tuple constraints: So long as variadicity is limited to a single, trailing tuple component, the corresponding step in unification - matching arity - is nearly trivial, and unification of types is likewise uncomplicated. 4. Considering varidiac elements of a tuple constraint in positions other than the tail: I don't have a confident analysis of everything that comes to mind, but I'm thinking this leads to bad outcomes, like an increase in the computational complexity of the unification algorithm, or obnoxiously esoteric rules for how constraints intersect. *>> [T (K, any), K comparable] describes the type set of 2-ary tuples that begin with a comparable element.> How would you see that as different from [T (comparable, any)] ?* The type parameter `K` might be significant elsewhere, e.g. `func F[T (K, any), K any](f func(T), input K)`. *> Would these two be equivalent too?> func F[K comparable, V any](f []func(K, V)) {}> func F[KV (comparable, any)](f []func(KV)) {}* Yes, and I can be sharper about "equivalent". I don't think that the type parameter lists should be equivalent. Tuple constraints capture essential information about grouping and position of types. But any slice-of-functions type should be equivalently valid or invalid for either. *func main() { for x := range FSeq(strconv.Atoi) { fmt.Println(reflect.TypeOf(x)) }}func FSeq[V (... any)](f func(string) V) iter.Seq[V] { return func(yield func(v V) bool)) { yield(f("1234")) }}* *If it wouldn't be valid, why not? If it's OK, what would it print?* My understanding is that how helpful type inference can be is not a question of specification. I would hope the code successfully infers without hints, and prints `(int, error)`. If not, by #64457 rules the hint would be `FSeq[(int, error)](strconv.Atoi)`; my pedantry would suggest spelling out the semantics `type R (Value int, Err error)` and `FSeq[R](strconv.Atoi)`. (I'm losing conviction on that pedantry...) On Monday, April 29, 2024 at 2:24:46 AM UTC-7 roger peppe wrote: > 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. >> > > FWIW this kind of restriction has no precedent in Go. Currently any type > may be unnamed. This restriction seems arbitrary and irregular to me. > >> >> *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) >> > Isn't this illegal according to your TupleType syntax above? > >> 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 >> > > By analogy with [T map[any]any] I'd expect the "any" there to be the > regular "any" type (as used outside generics) rather than the type > constraint "any". That is, my intuition would expect this to disallow a > (string, string) tuple, but my sense is that that's not what you're > suggesting. > > In general, a type constraint can be either a regular type (a type set) or > an interface type, but this doesn't seem like either, and looks to me like > it would significantly complicate the generics specification. > > >> [T (K, any), K comparable] describes the type set of 2-ary tuples that >> begin with a comparable element. >> > > How would you see that as different from [T (comparable, any)] ? > > >> >> 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)) { ... } > > > Would these two be equivalent too? > > func FK 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] >> > > It sounds like by your explanation here that this code should be valid: > > func main() { > for x := range FSeq(strconv.Atoi) { > fmt.Println(reflect.TypeOf(x)) > } > } > > func FSeq[V (... any)](f func(string) V) iter.Seq[V] { > return func(yield func(v V) bool)) { > yield(f("1234")) > } > } > > If it wouldn't be valid, why not? If it's OK, what would it print? > -- 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/a7482fb7-a130-4ba7-aad6-9f81910e7b95n%40googlegroups.com.