An mistake in tip spec?

393 views
Skip to first unread message

tapi...@gmail.com

unread,
Jan 5, 2022, 3:20:16 PM1/5/22
to golang-nuts

   interface{ int; any } // no specific types (intersection is empty)


Why the constraint has no specific types. Doesn't it specify int?

tapi...@gmail.com

unread,
Jan 6, 2022, 4:11:51 AM1/6/22
to golang-nuts


Should it be

    interface{ int | any } // no specific types (intersection is empty)

instead

Brian Candler

unread,
Jan 6, 2022, 10:15:06 AM1/6/22
to golang-nuts
1. interface { a;b } is intersection. The "Intersection" between two sets means things which exist in both sets simultaneously.
2. interface { a|b } is union.  "Union" means a set of things which which exist in set A or set B.

Quoting from the spec:
"the predeclared type any is an alias for the empty interface." 
"interface{} // no specific types"
"For an interface with type elements, 𝑆 is the intersection of the specific types of its type elements."

Can you see now?

tapi...@gmail.com

unread,
Jan 6, 2022, 11:47:52 AM1/6/22
to golang-nuts
The explanation is as what I think.
But what is your conclusion? Is it a mistake in spec?

Brian Candler

unread,
Jan 6, 2022, 12:35:06 PM1/6/22
to golang-nuts
No, the mistake is in your reading of the spec.  You are complaining about this line:

interface{ int; any } // no specific types (intersection is empty)

The spec makes it clear that:
1. "any" is short for "interface {}"
2. "interface {}" has no specific types
3. "interface { int; any }" is an intersection of specific types

You are taking the intersection of the set of one type (int) with the empty set, and therefore the result is the empty set.  Exactly as the comment says.

tapi...@gmail.com

unread,
Jan 6, 2022, 12:57:36 PM1/6/22
to golang-nuts
I don't think your conclusion is right. Otherwise, the following code doesn't compile, but it does.

type C interface{ int; any }

func f[T C](x byte) T {
        return T(x)
}

Brian Candler

unread,
Jan 6, 2022, 1:40:49 PM1/6/22
to golang-nuts
So to be more specific, I think you're asking why this code works:

Sorry, I made a mistake in interpreting the spec.  It also says:

"The type set of the empty interface is the set of all types."

Therefore, the type set of the intersection { int; any } is int - and hence f[int] is allowed.

But the set of specific types is empty:

"An interface specification which contains type elements defines a (possibly empty) set of specific types. Loosely speaking, these are the types T that appear in the interface definition in terms of the form T, ~T, or in unions of such terms."

The type set and the set of specific types can be different. Examples are given where one is empty and the other is non-empty:

interface{ int; m() } // [specific type] int (but type set is empty because int has no method m)
interface{ int; any } // no specific types (intersection is empty) [even though the type set is not empty]

Items in [italics/brackets] are my additions

tapi...@gmail.com

unread,
Jan 6, 2022, 1:40:52 PM1/6/22
to golang-nuts
On Thursday, January 6, 2022 at 8:35:06 PM UTC+8 Brian Candler wrote:
No, the mistake is in your reading of the spec.  You are complaining about this line:

interface{ int; any } // no specific types (intersection is empty)

The spec makes it clear that:
1. "any" is short for "interface {}"
2. "interface {}" has no specific types


I think your logic mistake here is that the operands of the union and intersection operations are type sets, instead of specific types.

tapi...@gmail.com

unread,
Jan 6, 2022, 1:44:05 PM1/6/22
to golang-nuts
This is true.

> interface{ int; any } // no specific types (intersection is empty) [even though the type set is not empty]

I don't think this is true. In my opinion, the method set of specific types of interface{ int; any } are both {int}.

tapi...@gmail.com

unread,
Jan 6, 2022, 1:45:35 PM1/6/22
to golang-nuts
sorry a typo:  it should be 

the method set and specific types of interface{ int; any } are both {int}.

tapi...@gmail.com

unread,
Jan 6, 2022, 2:05:10 PM1/6/22
to golang-nuts
sorry again. It should be "the type set and specific types of interface{ int; any } are both {int}."

tapi...@gmail.com

unread,
Jan 6, 2022, 3:19:40 PM1/6/22
to golang-nuts
On Thursday, January 6, 2022 at 9:40:52 PM UTC+8 tapi...@gmail.com wrote:
On Thursday, January 6, 2022 at 8:35:06 PM UTC+8 Brian Candler wrote:
No, the mistake is in your reading of the spec.  You are complaining about this line:

interface{ int; any } // no specific types (intersection is empty)

The spec makes it clear that:
1. "any" is short for "interface {}"
2. "interface {}" has no specific types


I think your logic mistake here is that the operands of the union and intersection operations are type sets, instead of specific types.

This conclusion is not very precise. More precisely, the operands of the union and intersection operations
could be either type set or specific types, but interface types don't participate in calculations of specific types.

tapi...@gmail.com

unread,
Jan 6, 2022, 3:37:55 PM1/6/22
to golang-nuts
On Thursday, January 6, 2022 at 11:19:40 PM UTC+8 tapi...@gmail.com wrote:
On Thursday, January 6, 2022 at 9:40:52 PM UTC+8 tapi...@gmail.com wrote:
On Thursday, January 6, 2022 at 8:35:06 PM UTC+8 Brian Candler wrote:
No, the mistake is in your reading of the spec.  You are complaining about this line:

interface{ int; any } // no specific types (intersection is empty)

The spec makes it clear that:
1. "any" is short for "interface {}"
2. "interface {}" has no specific types


I think your logic mistake here is that the operands of the union and intersection operations are type sets, instead of specific types.

This conclusion is not very precise. More precisely, the operands of the union and intersection operations
could be either type set or specific types, but interface types don't participate in calculations of specific types.

This is still not precise. More precisely speaking, in calculations of specific types,
interface types don't participate in intersection operations,
and only "any" (interface{}) is allowed to participate in union operations.
The result of a union operation with any as an operand is a blank set.

Jason Phillips

unread,
Jan 6, 2022, 4:11:45 PM1/6/22
to golang-nuts
@Brian

> interface{ int; m() } // [specific type] int (but type set is empty because int has no method m)
> interface{ int; any } // no specific types (intersection is empty) [even though the type set is not empty]

As noted in the first paragraph of the spec, the set of specific types is calculated by only considering type elements in the interface. Given that, I think the spec note is wrong in the example being discussed. It should be "int". Also, regardless of the answer, surely the specific types for "interface{ int; m() }" and "interface{ int; any}" are always the same?

Jason Phillips

unread,
Jan 6, 2022, 4:34:02 PM1/6/22
to golang-nuts
And by "first paragraph of the spec" I mean "first paragraph of the Structure of interfaces section of the spec". Apologies.

Jason Phillips

unread,
Jan 6, 2022, 4:57:59 PM1/6/22
to golang-nuts
I take my argument back, I agree with Brian here. According to the spec "interface{}" is a type element because it contains no methods. So the intersection of the specific types "interface{}" and "int" is an empty set. But the set of specific types is "int".

Axel Wagner

unread,
Jan 6, 2022, 4:59:43 PM1/6/22
to golang-nuts
• From the definition of type elements, we can see that an embedded interface type is a type element. Therefore, `any` is a type element.
• `any` is an alias for `interface{}`, therefore it is a type without any type elements, therefore the set of its specific types is empty ("For an interface with no type elements, 𝑆 is the empty set.").
• `interface{ int; any }` is a type with two type elements. "For an interface with type elements, 𝑆 is the intersection of the specific types of its type elements.". Intersecting with an empty set (the specific types of `any`) gives an empty set
• Therefore, the set of specific types of `interface{ int; any }` is the empty set.

--
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/cda21095-d57e-4f9d-8853-4a6a7e683953n%40googlegroups.com.

Jason Phillips

unread,
Jan 6, 2022, 5:00:47 PM1/6/22
to golang-nuts
> But the set of specific types is "int".
But the *type set* is "int"...

Axel Wagner

unread,
Jan 6, 2022, 5:13:38 PM1/6/22
to Ian Lance Taylor, Robert Griesemer, golang-nuts
On Thu, Jan 6, 2022 at 1:58 PM tapi...@gmail.com <tapi...@gmail.com> wrote:
I don't think your conclusion is right. Otherwise, the following code doesn't compile, but it does.

type C interface{ int; any }

func f[T C](x byte) T {
        return T(x)
}

I agree that the compiler and spec seem to disagree here. It seems to me that by the spec, this should not be allowed, as C has no specific types. Note that this doesn't mean the spec is wrong - it can also mean the compiler is wrong.
 

On Thursday, January 6, 2022 at 8:35:06 PM UTC+8 Brian Candler wrote:
No, the mistake is in your reading of the spec.  You are complaining about this line:

interface{ int; any } // no specific types (intersection is empty)

The spec makes it clear that:
1. "any" is short for "interface {}"
2. "interface {}" has no specific types
3. "interface { int; any }" is an intersection of specific types

You are taking the intersection of the set of one type (int) with the empty set, and therefore the result is the empty set.  Exactly as the comment says.

On Thursday, 6 January 2022 at 11:47:52 UTC tapi...@gmail.com wrote:
On Thursday, January 6, 2022 at 6:15:06 PM UTC+8 Brian Candler wrote:
1. interface { a;b } is intersection. The "Intersection" between two sets means things which exist in both sets simultaneously.
2. interface { a|b } is union.  "Union" means a set of things which which exist in set A or set B.

Quoting from the spec:
"the predeclared type any is an alias for the empty interface." 
"interface{} // no specific types"
"For an interface with type elements, 𝑆 is the intersection of the specific types of its type elements."

Can you see now?

The explanation is as what I think.
But what is your conclusion? Is it a mistake in spec?

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

Jason Phillips

unread,
Jan 6, 2022, 5:14:55 PM1/6/22
to golang-nuts
It does seem like the spec could be more clear for this specific example. The type set of "interface{ int; m() }" is called out explicitly so the same seems helpful for "interface{ int; any }".

Brian Candler

unread,
Jan 6, 2022, 6:02:07 PM1/6/22
to golang-nuts
On Thursday, 6 January 2022 at 17:13:38 UTC axel.wa...@googlemail.com wrote:
On Thu, Jan 6, 2022 at 1:58 PM tapi...@gmail.com <tapi...@gmail.com> wrote:
I don't think your conclusion is right. Otherwise, the following code doesn't compile, but it does.

type C interface{ int; any }

func f[T C](x byte) T {
        return T(x)
}

I agree that the compiler and spec seem to disagree here. It seems to me that by the spec, this should not be allowed, as C has no specific types.

But is that true?  "Type set" and "specific types" are two different concepts, and "specific types" are only required in certain contexts, which are called out in the spec.

It seems that function f[T C] will accept any type which is in the *type set* of C - is that not reasonable?

This does of course highlight how complex the language has become with the introduction of generics.

Axel Wagner

unread,
Jan 6, 2022, 6:23:54 PM1/6/22
to Brian Candler, golang-nuts
On Thu, Jan 6, 2022 at 7:02 PM Brian Candler <b.ca...@pobox.com> wrote:
On Thursday, 6 January 2022 at 17:13:38 UTC axel.wa...@googlemail.com wrote:
On Thu, Jan 6, 2022 at 1:58 PM tapi...@gmail.com <tapi...@gmail.com> wrote:
I don't think your conclusion is right. Otherwise, the following code doesn't compile, but it does.

type C interface{ int; any }

func f[T C](x byte) T {
        return T(x)
}

I agree that the compiler and spec seem to disagree here. It seems to me that by the spec, this should not be allowed, as C has no specific types.

But is that true?  "Type set" and "specific types" are two different concepts, and "specific types" are only required in certain contexts, which are called out in the spec.

Correct. One of those is determining if the conversion `T(x)` is legal, if `T` is a type parameter. Specifically, it only makes it legal if `T` is a type parameter with specific types.

It seems that function f[T C] will accept any type which is in the *type set* of C - is that not reasonable?

Yes. But the issue isn't the instantiation of `f` (in fact, `f` is not instantiated in this code), it's the definition.
 

This does of course highlight how complex the language has become with the introduction of generics.

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

Axel Wagner

unread,
Jan 6, 2022, 6:52:30 PM1/6/22
to golang-nuts
To elaborate a bit about the differences between "the type set" and "the set of specific types" and the purpose of the latter - at least as I understand it:

The actual type set is hard to compute, in general, so using it to define the set of allowed operations on a type is impractical. The easier to calculate set of specific types was introduced, I believe, to fix this problem. It should always be a subset of the actual type set. By being easy to compute, it is useful to determine what operations are allowed on a type parameter in a function body - we can calculate the set of specific types and just verify that an operation is allowed on all of them. As it's a subset of the true type set, this will never allow an operation, which is not allowed by all elements in the type set. But it might disallow operations, which the type set would allow.

OTOH, checking if any given type is an element of the type set is easy. So the type set is still useful to determine if a function can be *instantiated* using a given type - as in that case, the type is known and we just need to verify it's in the type set.

In short: The type set is used to validate instantiations of a generic function, while the set of specific types is used to validate the body of a generic function.

In this case, the set of specific types is smaller, than the actual type set. The type set is `{int}`, but the set of specific types is empty. Therefore, the function should be able to be instantiated with `int`, but the function body should not be allowed to do any operation on the type parameter (which is not allowed by all types). Which is why I'm inclined to believe that the compiler is wrong here - if we think this should work, we probably shouldn't have the set of specific types to begin with.

It may seem unfortunate to end with an empty set of specific types (and thus empty set of allowed operations), but it's dictated by the computational difficulty of the problem. And, ultimately, this should only really carry in cases which are pathological. i.e. the way to solve this limitation, is to simply not use `interface{ int; any }`, but instead use `interface { int }`, which has the same type set and a non-empty set of specific types (equal to its type set).

Ian Lance Taylor

unread,
Jan 6, 2022, 7:18:35 PM1/6/22
to Axel Wagner, golang-nuts
On Thu, Jan 6, 2022 at 8:59 AM 'Axel Wagner' via golang-nuts
<golan...@googlegroups.com> wrote:
>
> • From the definition of type elements, we can see that an embedded interface type is a type element. Therefore, `any` is a type element.
> • `any` is an alias for `interface{}`, therefore it is a type without any type elements, therefore the set of its specific types is empty ("For an interface with no type elements, 𝑆 is the empty set.").
> • `interface{ int; any }` is a type with two type elements. "For an interface with type elements, 𝑆 is the intersection of the specific types of its type elements.". Intersecting with an empty set (the specific types of `any`) gives an empty set
> • Therefore, the set of specific types of `interface{ int; any }` is the empty set.

That doesn't seem right. "any" is an interface type and writing
"interface { any }" just embeds the empty interface, which has no
effect.

I think the spec is incorrect here (and gri already sent
https://golang.org/cl/375799 for this).

But maybe there is something else wrong with the description of specific types.

Ian
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAEkBMfGV9tfn%2BZfRKs%3DyX2uybnUfxWswRUHww%2B%2BdUQsvP%2B_4FA%40mail.gmail.com.

Axel Wagner

unread,
Jan 6, 2022, 7:33:12 PM1/6/22
to Ian Lance Taylor, golang-nuts
On Thu, Jan 6, 2022 at 8:18 PM Ian Lance Taylor <ia...@golang.org> wrote:
On Thu, Jan 6, 2022 at 8:59 AM 'Axel Wagner' via golang-nuts
<golan...@googlegroups.com> wrote:
>
> • From the definition of type elements, we can see that an embedded interface type is a type element. Therefore, `any` is a type element.
> • `any` is an alias for `interface{}`, therefore it is a type without any type elements, therefore the set of its specific types is empty ("For an interface with no type elements, 𝑆 is the empty set.").
> • `interface{ int; any }` is a type with two type elements. "For an interface with type elements, 𝑆 is the intersection of the specific types of its type elements.". Intersecting with an empty set (the specific types of `any`) gives an empty set
> • Therefore, the set of specific types of `interface{ int; any }` is the empty set.

That doesn't seem right.  "any" is an interface type and writing
"interface { any }" just embeds the empty interface, which has no
effect.

I think it has no effect on the type set. But it has an effect on the set of specific types.

I believe both the rule "for an interface type without type elements S is empty" and the rule "for an interface type with type elements, S is the intersection of the specific types of its type elements" are vital to the working of S - they are, what makes the set easily computable.

To answer the question from your comment in the CL: I believe the grammar is very unambiguous about `any` being a type element:

InterfaceType = "interface" "{" { InterfaceElem ";" } "}" .
InterfaceElem = MethodElem | TypeElem .
MethodElem = MethodName Signature .
MethodName = identifier .
TypeElem = TypeTerm { "|" TypeTerm } .
TypeTerm = Type | UnderlyingType .
UnderlyingType = "~" Type .
 
There is no signature, so it's not a MethodElem. Therefore it must be a TypeElem.

Ian Lance Taylor

unread,
Jan 6, 2022, 7:38:03 PM1/6/22
to Axel Wagner, Robert Griesemer, golang-nuts
On Thu, Jan 6, 2022 at 11:32 AM Axel Wagner
<axel.wa...@googlemail.com> wrote:
>
> On Thu, Jan 6, 2022 at 8:18 PM Ian Lance Taylor <ia...@golang.org> wrote:
>>
>> On Thu, Jan 6, 2022 at 8:59 AM 'Axel Wagner' via golang-nuts
>> <golan...@googlegroups.com> wrote:
>> >
>> > • From the definition of type elements, we can see that an embedded interface type is a type element. Therefore, `any` is a type element.
>> > • `any` is an alias for `interface{}`, therefore it is a type without any type elements, therefore the set of its specific types is empty ("For an interface with no type elements, 𝑆 is the empty set.").
>> > • `interface{ int; any }` is a type with two type elements. "For an interface with type elements, 𝑆 is the intersection of the specific types of its type elements.". Intersecting with an empty set (the specific types of `any`) gives an empty set
>> > • Therefore, the set of specific types of `interface{ int; any }` is the empty set.
>>
>> That doesn't seem right. "any" is an interface type and writing
>> "interface { any }" just embeds the empty interface, which has no
>> effect.
>
>
> I think it has no effect on the type set. But it has an effect on the set of specific types.
>
> I believe both the rule "for an interface type without type elements S is empty" and the rule "for an interface type with type elements, S is the intersection of the specific types of its type elements" are vital to the working of S - they are, what makes the set easily computable.
>
> To answer the question from your comment in the CL: I believe the grammar is very unambiguous about `any` being a type element:
>
>> InterfaceType = "interface" "{" { InterfaceElem ";" } "}" .
>> InterfaceElem = MethodElem | TypeElem .
>> MethodElem = MethodName Signature .
>> MethodName = identifier .
>> TypeElem = TypeTerm { "|" TypeTerm } .
>> TypeTerm = Type | UnderlyingType .
>> UnderlyingType = "~" Type .
>
>
> There is no signature, so it's not a MethodElem. Therefore it must be a TypeElem.

Yeah, I think there is something wrong in the current spec. It can't
be the case that embedding an interface eliminates all specific types.

Ian

Brian Candler

unread,
Jan 6, 2022, 8:13:44 PM1/6/22
to golang-nuts
On Thursday, 6 January 2022 at 18:52:30 UTC axel.wa...@googlemail.com wrote:
The actual type set is hard to compute, in general, so using it to define the set of allowed operations on a type is impractical. The easier to calculate set of specific types was introduced, I believe, to fix this problem. It should always be a subset of the actual type set.

 Is that necessarily true? The spec appears to have an example where the specific types are a superset of the type set:

-----
Examples of interfaces with their specific types:
...
interface{ int; m() } // int (but type set is empty because int has no method m)
-----

Axel Wagner

unread,
Jan 6, 2022, 9:00:24 PM1/6/22
to golang-nuts
On Thu, Jan 6, 2022 at 9:14 PM Brian Candler <b.ca...@pobox.com> wrote:
Is that necessarily true? The spec appears to have an example where the specific types are a superset of the type set:

-----
Examples of interfaces with their specific types:
...
interface{ int; m() } // int (but type set is empty because int has no method m)
-----

You are right. I don't think the foundational argument is touched by that, as the type set is only smaller because it is shrunken down by methods, which are considered separately (interface types with method sets can't appear in unions). 

Or maybe it is. TBQH, I did quite a lot of thinking about this topic a while back (when type sets where first proposed) and I was kind of hoping to be done with all of that. In fact, this is the first time I really tried reading the new parts of the spec and I feel re-affirmed in my belief that the effort we have to go through (both in terms of the room it takes up in the spec and the amount of effort we invest in trying to figure out if what we are doing is right) just to make operators work is not really commensurate with the benefit.


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

Axel Wagner

unread,
Jan 6, 2022, 9:23:31 PM1/6/22
to Ian Lance Taylor, Robert Griesemer, golang-nuts
The more I think about it, the more I'm inclined to agree that there is something wrong with the spec (and maybe the idea of sets of specific types in general).
Because what's the set of specific types of `interface { int | any }`? From the definition, it should be `int` - the union with an empty set is a no-op. But the *type set* of that is all types. Which means that this should be allowed, as per the spec, which is obviously nonsense:

type C interface {
    int | any
}

func F[T C](v T) {
    fmt.Println(int(v))
}

So, logically, the type set of an interface type without type elements should really be the set of all types (or rather "the set of all underlying types"), not the empty set, for the algorithm to make sense. In that case, the specific types of `int | any` are the set of all types and the specific types of `int ; any` is `int`.

I'm not sure if this would be sound, though, as we now get back to the situation of reducing back from the set of all types to some subset. Maybe the new restrictions put in place (namely that interfaces with methods can't appear in unions) are enough to make this easy to manage. I don't know. Thinking through that would take time.

gri

unread,
Jan 7, 2022, 1:34:41 AM1/7/22
to golang-nuts
Thanks for raising this issue. This is clearly a bug in the spec, or at the very least "an imprecision". Hopefully this is better: https://go-review.googlesource.com/c/go/+/375799 .

The set of specific types is an approximation for the actual type set. In practice we only need to consider those explicitly mentioned types. If the type set is infinite, we can't do much (with respect to operations). To make that simple elsewhere in the spec we want the set of specific types be empty in that case. An alternative approach might be to say the set of specific types is infinite in that case and exclude such infinite sets where we look at specific types.

- gri

tapi...@gmail.com

unread,
Jan 7, 2022, 3:53:49 AM1/7/22
to golang-nuts
Thanks for the reply, gri.

The latest generics design and implementation are much more beautiful than I expected, though I still think there are many restrictions.

BTW, it looks the following line in spec becomes depreciated now, as now any type could be embedded in interfaces.

In a slightly more general form an interface T may use a (possibly qualified) interface type name E as an interface element. This is called embedding interface E in T.

Axel Wagner

unread,
Jan 7, 2022, 6:08:27 AM1/7/22
to gri, golang-nuts
On Fri, Jan 7, 2022 at 2:35 AM 'gri' via golang-nuts <golan...@googlegroups.com> wrote:
Thanks for raising this issue. This is clearly a bug in the spec, or at the very least "an imprecision". Hopefully this is better: https://go-review.googlesource.com/c/go/+/375799 .

ISTM this still fails for `int | any`, which according to the rules has specific types `int`, but should have no specific types.
 
--
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.

Robert Griesemer

unread,
Jan 8, 2022, 2:32:35 AM1/8/22
to Axel Wagner, golang-nuts
Indeed. There's no shortcuts possible here. Hopefully this works better (and closely matches the implementation):
- gri

Brian Candler

unread,
Jan 8, 2022, 10:02:17 AM1/8/22
to golang-nuts
Now my head is spinning:

interface{ int; m() }          // int (but type set is empty because int has no method m)
interface{ ~int; m() }         // int (but type set is infinite because there are many int types with method m)

Reply all
Reply to author
Forward
0 new messages