*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.*
Formally, I'd argue `R` is a valid instantiation of the tuple constraint 
`(... any)`. `(... any)` is the entire universe of tuples, and `R` is a 
tuple type. Additionally, Go already recognizes the two tuples in a 
signature 
(https://github.com/golang/go/blob/master/src/go/types/signature.go) `func 
(string) (int, error)` on a more implicit basis.

There is an unstated principle that follows from a notion of promotion. If 
unnamed->named tuple type promotion is free and automatic, it makes sense 
to freely unify any occurrences of implicit tuple type `(int, error)` with 
`R`, but not to unify two congruent named types. I'd thought about this, 
but a quirk I didn't consider is that a type parameter might unify to a 
named type in composition, but use an implicit type at the point of 
instantiation - I think it's justifiable to just promote down to the 
implicit type in this case.

I wonder that this might be a question that #64457 would run into as well, 
if it were open, because it also allowed named tuple types.

---

A tangent on what's formally possible, currently: 
https://go.dev/play/p/7JRCfkjNpJk
We can write:
- A function `F`
- that accepts as input, a function `f`
- accepting 1 string and returning 2 things (`T`),
- parameteric over they types of 2 things (`K` and `V`)
- and prints the structural signature type.

This does parameterize on a single type `T`, with some additional 
complication bridging (3) and (4): a type `KV`, itself parametrized on `K` 
and `V`, leveraging the fact that tuples already exist in the type system 
as second-class components of function signatures.

Without tuple constraints:
1. `func F[T KV[K, V], K any, V any](f T)`

With tuple constraints:
2. `func F[T (any, any)](f func(string) T)` // not variadic
3. `func F[Out (... any)](f func(string) Out)` // variadic tuple 
constraint; this matches the formal limit of #66651
4. `func F[In (... any), Out (... any)](f func(In) Out)` multiple variadic 
tuple constraints
On Tuesday, April 30, 2024 at 8:12:09 AM UTC-7 roger peppe wrote:

> On Mon, 29 Apr 2024 at 22:06, Andrew Harris <harris...@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...@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...@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/d866827a-4ef2-4576-950a-1ffb35f2fdffn%40googlegroups.com.

Reply via email to