Update to generics proposal

619 views
Skip to first unread message

Ian Lance Taylor

unread,
Apr 1, 2021, 7:00:06 PM4/1/21
to golang-nuts
We've just posted a potential update to the generics proposal at
https://golang.org/issue/45346. This clarifies and simplifies the
type lists that appear in interfaces, and let's us drop the "type"
keyword used to mark such lists.

Ian

Eltjon Metko

unread,
Apr 3, 2021, 7:48:30 PM4/3/21
to golang-nuts
I was fully expecting for floodgates of comments to open again but it seems we have reached a point of maturity in the generics proposal. 
The new proposal really makes the intent much clearer both on the exact vs underlying type match front  and the syntax gives us a more familiar union intent. 
So in that light this proposal is a welcome change by itself. 

If this would allow us then later to switch on the matched type (preferably without type assertion, along the lines of the comment in https://github.com/golang/go/issues/45346#issuecomment-812557199
than it would make this implementation of  generics much more useful.  

K. Alex Mills

unread,
Apr 4, 2021, 8:18:18 AM4/4/21
to Ian Lance Taylor, golang-nuts
This seems like a welcome change and leaves the door open for the future inclusion of type unions. Nicely done!

Regarding this approximation operator, I have a question about the intended meaning of:

~struct{
  F int
  A string
}

Would this match the following types?

type A struct{
  F int
  A string
}

type B struct{
  A string
  F int
}

type C struct{
  X int
  Y string
}

type D struct{
  F int
  A string
  B float
}

My guess is that we're talking about an exact type match, so it would definitely match A, I'm not sure about B without checking the compiler, and I don't think it would match either C or D under the current proposal.

I think it could potentially be useful to have this approximation operator match type D, although that would require modifying the proposal to specify some notion of "field sets", which seems like it could be a departure from how Go currently views structs and therefore may introduce further complications I am not aware of. Just curious if it's already been considered and discarded.

Thanks!

--
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/CAOyqgcVEx53D7YmFRBe43_jcrVSt%2BPbDEYjc4EZnpe1YyBNAtg%40mail.gmail.com.

Axel Wagner

unread,
Apr 4, 2021, 8:34:32 AM4/4/21
to golang-nuts
On Sun, Apr 4, 2021 at 2:18 PM K. Alex Mills <k.alex...@gmail.com> wrote:
This seems like a welcome change and leaves the door open for the future inclusion of type unions. Nicely done!

Regarding this approximation operator, I have a question about the intended meaning of:

~struct{
  F int
  A string
}

Would this match the following types?

type A struct{
  F int
  A string
}

Yes.
 

type B struct{
  A string
  F int
}

No. The underlying types must be identical:

Two struct types are identical if they have the same sequence of fields, and if corresponding fields have the same names, and identical types, and identical tags.
 
The order must match.

A good litmus test is that if A's type is a defined type and B's type isn't, then B is assignable to A, if they have identical underlying types.
You can test this like this playground snippet.

type C struct{
  X int
  Y string
}

No, fields must be named the same.

type D struct{
  F int
  A string
  B float
}

No, numbers of fields must be the same.

My guess is that we're talking about an exact type match, so it would definitely match A, I'm not sure about B without checking the compiler, and I don't think it would match either C or D under the current proposal.

I think it could potentially be useful to have this approximation operator match type D, although that would require modifying the proposal to specify some notion of "field sets", which seems like it could be a departure from how Go currently views structs and therefore may introduce further complications I am not aware of. Just curious if it's already been considered and discarded.

Thanks!

On Thu, Apr 1, 2021, 5:59 PM Ian Lance Taylor <ia...@golang.org> wrote:
We've just posted a potential update to the generics proposal at
https://golang.org/issue/45346.  This clarifies and simplifies the
type lists that appear in interfaces, and let's us drop the "type"
keyword used to mark such lists.

Ian

--
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/CAOyqgcVEx53D7YmFRBe43_jcrVSt%2BPbDEYjc4EZnpe1YyBNAtg%40mail.gmail.com.

--
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.

roger peppe

unread,
Apr 4, 2021, 4:39:46 PM4/4/21
to Eltjon Metko, golang-nuts
On Sun, 4 Apr 2021 at 00:48, Eltjon Metko <eme...@gmail.com> wrote:
I was fully expecting for floodgates of comments to open again but it seems we have reached a point of maturity in the generics proposal. 
The new proposal really makes the intent much clearer both on the exact vs underlying type match front  and the syntax gives us a more familiar union intent. 
So in that light this proposal is a welcome change by itself. 

If this would allow us then later to switch on the matched type (preferably without type assertion, along the lines of the comment in https://github.com/golang/go/issues/45346#issuecomment-812557199
than it would make this implementation of  generics much more useful.  

FYI because discussion on that was starting to disrupt the original proposal, I created
a specific issue for the type switch construct suggested in that comment:


yiyus

unread,
Apr 5, 2021, 4:58:26 PM4/5/21
to golang-nuts
I may have missed something, but I have problems to understand what is the goal of the proposal. It says:

> The purpose of introducing type lists in the generics proposal was to specify the operations available to type parameters in parameterized functions.

I do not see how approximation elements help to solve this problem. A type and its underlying type support exactly the same operations, so it should not be necessary to make the distinction. If union elements represented the set of types which allow all the operations common to all types in the union, the above goal would be fulfilled.

A few times in this thread, it has been said that it can be useful to explicitly specify the allowed types. But I think that if this is indeed a goal, it needs to be better justified than referring to previous discussions. In particular, I did not find anything in #41716  that convinces me that explicit types are useful to define type constraints.

I think we will agree interfaces in go are very useful because they define behavior instead of a fixed set of types, I would like to know what makes the case for constraints different.

Ian Lance Taylor

unread,
Apr 5, 2021, 5:08:00 PM4/5/21
to yiyus, golang-nuts
On Mon, Apr 5, 2021 at 1:58 PM yiyus <yiyu...@gmail.com> wrote:
>
> I may have missed something, but I have problems to understand what is the goal of the proposal. It says:
>
> > The purpose of introducing type lists in the generics proposal was to specify the operations available to type parameters in parameterized functions.
>
> I do not see how approximation elements help to solve this problem. A type and its underlying type support exactly the same operations, so it should not be necessary to make the distinction. If union elements represented the set of types which allow all the operations common to all types in the union, the above goal would be fulfilled.
>
> A few times in this thread, it has been said that it can be useful to explicitly specify the allowed types. But I think that if this is indeed a goal, it needs to be better justified than referring to previous discussions. In particular, I did not find anything in #41716 that convinces me that explicit types are useful to define type constraints.
>
> I think we will agree interfaces in go are very useful because they define behavior instead of a fixed set of types, I would like to know what makes the case for constraints different.

Thanks for the comment.

I agree that approximation elements do not solve the problem of
specifying which operations are available to type parameters.

In the currently published generics proposal, not including the newly
proposed issue #45346, we permit both `type int" and "type MyInt".
But although they look quite similar they mean different things.
Writing "type int" matches all types whose underlying type is "int".
Writing "type MyInt" matches only "MyInt". Both are meaningful, but
it's odd that two constructs that look so similar mean different
things. Adding approximation elements fixes that problem.

More generally, if we omit approximation elements, it's a bit odd that
if I write "int" I mean "an infinite set of types including int". It
seems clearer to require people to explicitly indicate that they want
to match the infinite set of types.

Ian

yiyus

unread,
Apr 5, 2021, 6:02:32 PM4/5/21
to golang-nuts
> More generally, if we omit approximation elements, it's a bit odd that
> if I write "int" I mean "an infinite set of types including int".  It
> seems clearer to require people to explicitly indicate that they want
> to match the infinite set of types.

What I propose is not that "int means an infinite set of types including int", what I propose is that interface{int} means the types that support the operations of the int type. The fact that this "int" is inside an interface definition is important. From my point of view, defining an interface already is an explicit way to indicate that you want to match an infinite set of types.

This would solve, in my opinion, the problem of interface{int} and interface{MyInt} looking similar but with different meanings. They would mean the same, and I think that is fine. Could you (or someone else) give an example of when would it be useful that they mean different things in the context of generic constraints?

Thank you for answering (and your work in the proposal, it keeps getting better!).

roger peppe

unread,
Apr 5, 2021, 6:14:40 PM4/5/21
to yiyus, golang-nuts
On Mon, 5 Apr 2021, 21:58 yiyus, <yiyu...@gmail.com> wrote:
 A type and its underlying type support exactly the same operations

FWIW I don't believe that's the case. A type may have methods (each with at least one corresponding operation) that its underlying type does not.


Ian Lance Taylor

unread,
Apr 5, 2021, 6:35:26 PM4/5/21
to yiyus, golang-nuts
On Mon, Apr 5, 2021 at 3:02 PM yiyus <yiyu...@gmail.com> wrote:
>
> > More generally, if we omit approximation elements, it's a bit odd that
> > if I write "int" I mean "an infinite set of types including int". It
> > seems clearer to require people to explicitly indicate that they want
> > to match the infinite set of types.
>
> What I propose is not that "int means an infinite set of types including int", what I propose is that interface{int} means the types that support the operations of the int type. The fact that this "int" is inside an interface definition is important. From my point of view, defining an interface already is an explicit way to indicate that you want to match an infinite set of types.
>
> This would solve, in my opinion, the problem of interface{int} and interface{MyInt} looking similar but with different meanings. They would mean the same, and I think that is fine. Could you (or someone else) give an example of when would it be useful that they mean different things in the context of generic constraints?

Given

type MyInt int
type MyInt2 int

then interface{ int } as a constraint will accept either MyInt or
MyInt2 as a type argument, but interface { MyInt } will only accept
MyInt as a type argument.

Or if that is not the case, if interface { MyInt } does accept MyInt2,
then I guess you mean that interface { MyInt } will accept any type
argument whose underlying type is the same as the underlying type of
MyInt. But that seems strange. There is no connection between MyInt
and MyInt2, except that they happen to be defined in terms of the same
underlying type.

Ian

yiyus

unread,
Apr 5, 2021, 9:03:54 PM4/5/21
to golang-nuts
>  then I guess you mean that interface { MyInt } will accept any type
> argument whose underlying type is the same as the underlying type of
> MyInt. But that seems strange. There is no connection between MyInt
> and MyInt2, except that they happen to be defined in terms of the same
> underlying type.

This is an excellent example. Indeed, I think that MyInt and MyInt2 should satisfy the same constraints (because they support exactly the same operations). Just like MyString and MyString2 will implement the Stringer interface if they happen to have a String method, and there is no way to constrain the types which can implement Stringer, I think that any type that can be used in a generic function like an int should satisfy interface{int} (and therefore also interface{MyInt} and interface{MyInt2}).

@rog: yes, you are right. For example, interface{MyInt} would be different from interface{int} if MyInt had any method. Then, under my proposal, in order to fulfill the interface{MyInt} constraint, it would be enough to have the same methods and support the same operations.

Thomas Bushnell BSG

unread,
Apr 6, 2021, 9:57:18 AM4/6/21
to yiyus, golang-nuts
On Mon, Apr 5, 2021 at 9:04 PM yiyus <yiyu...@gmail.com> wrote:
>  then I guess you mean that interface { MyInt } will accept any type
> argument whose underlying type is the same as the underlying type of
> MyInt. But that seems strange. There is no connection between MyInt
> and MyInt2, except that they happen to be defined in terms of the same
> underlying type.

This is an excellent example. Indeed, I think that MyInt and MyInt2 should satisfy the same constraints (because they support exactly the same operations). Just like MyString and MyString2 will implement the Stringer interface if they happen to have a String method, and there is no way to constrain the types which can implement Stringer, I think that any type that can be used in a generic function like an int should satisfy interface{int} (and therefore also interface{MyInt} and interface{MyInt2}).

But they don't necessarily support exactly the same operations if they have different methods. So maybe you mean that MyInt and MyInt2 should satisfy the same constraints if they have the same underlying type and happen to support the same method sets.  

yiyus

unread,
Apr 6, 2021, 10:04:23 AM4/6/21
to golang-nuts
I was specifically talking about Ian's example, where no methods are defined.
Reply all
Reply to author
Forward
0 new messages