On Mon, 29 Apr 2024 at 22:06, Andrew Harris <harris.and...@gmail.com> wrote:
> 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. > Note added emphasis above. > > > > > > > > > > > *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 pedantry would suggest spelling out the semantics `type R (Value int, > Err error)` and `FSeq[R](strconv.Atoi)`. I don't really understand how that could work, tbh. In `FSeq[R]` it seems like you're passing a single type parameter of type R, so the V type parameter would have a single member of type R. But then that arity (1) wouldn't match the arity of the argument count to strconv (2). That is, given `FSeq[R]`, I'd expect it to take an argument of type `func(string) R` not `func(string) (int, error)` but if that's the case, the example wouldn't work AFAICS. It seems to me that this conflation of sometimes-named tuples and value-lists is a significant flaw in the suggested semantics. cheers, rog. On Mon, 29 Apr 2024 at 22:06, Andrew Harris <harris.and...@gmail.com> wrote: > > *> 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 > <https://groups.google.com/d/msgid/golang-nuts/a7482fb7-a130-4ba7-aad6-9f81910e7b95n%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/CAJhgacgYSsa6GzSbW1_C8H--K7EjRJajUKBXodUw1nWUBZUzbQ%40mail.gmail.com.