On Fri, Jun 19, 2020 at 9:31 AM Bryan C. Mills <
bcm...@google.com> wrote:
>
> On Fri, Jun 19, 2020 at 1:30 AM Ian Lance Taylor <
ia...@golang.org> wrote:
>>
>> This code is acting as though, if ordinary interface types could have
>> type lists, it would be OK to write
>>
>> func Add2(x, y SmallInt) SmallInt { return x + y }ᵢ
>>
>> That is not OK, even though SmallInt has a type list. Even though
>> every type in theSmallInt type list supports +, they don't support +
>> with every other type in the type list.
>
>
> Yes, that is exactly my point: the fact that that program is invalid implies that type-list interfaces are not consistent with the semantics of other interface types.
I'm sure that I don't really understand what you are saying.
But from my perspective using a type list in an interface type used as
a type constraint permits certain operations in that generic function.
That is a specific feature of generic functions. You seem to be
trying to extend that capability to non-generic functions. But that
doesn't work.
A generic function permits saying that certain values are exactly the
same type. There is no way to say that using interface types in a
non-generic function.
The fact that this function is invalid:
func Add2(x, y SmallInt) SmallInt { return x + y }
is no more interesting than the fact that this function is invalid:
func Add2(type T1 SmallInt, T2 SmallInt)(x T1, y T2) T1 { return T1(x + y) }
You can only use the + operator if you have the same underlying type
on both sides. Neither of these variants of Add2 guarantee that.
Only a generic function can guarantee that, and that guarantee is
entirely separate from whether constraints are interfaces, or
contracts, or something else entirely.
> As a run-time interface type, MediumInt clearly must implement itself (that is a fundamental property of interface types), and SmallInt (I claim) must also implement MediumInt because the concrete values it allows are a strict subset of the values allowed for MediumInt. (Otherwise, interface types seem incoherent as a concept.)
>
> The constraint-satisfaction rule for ordinary (non-type-list) interfaces, both in the current draft design and in FGG, is very simple: “Implementing a constraint is simply implementing the interface type.” (In the FGG paper, constraint-satisfaction is formalized as the judgement “Φ :=Δ φ”, which is defined using a single, uniform rule.)
>
> However, type-list interfaces in the current design have a different, more complex rule: “[the type argument must implement the methods in the interface type and] the underlying type of the type argument must be identical to the underlying type of one of the types in the type list.” That is a much stronger requirement than “simply implementing the interface type”, and it implies that for an interface I that happens to include a type-list, there is no way to express the simple constraint “T implements interface I” without also adding the secondary constraint that “the underlying type of [T] must be … one of types in the type list”.
>
> Similarly, the type-checking rule for non-type-list interfaces is simple: “assuming that the type arguments are distinct types that each implement their constraint interfaces, check the body of the declaration”. In contrast, the type-checking rule for type-list interfaces has an extra condition: “assuming that the type arguments are distinct type that each implement their constraint interfaces, and assuming that each argument whose constraint is a type-list has an underlying type identical to one of the types in the list, check the body of the declaration”.
That all sounds right to me.
> Because the rules for type-list interfaces always have an extra condition beyond “simply implementing the interface type”, we would be locked into at least one of the following limitations on the evolution of the language:
>
> A type-list interface can never be used as a type argument.
> Or, a type parameter of an interface type can never be allowed as the constraint of another type parameter, nor embedded in the constraint of another type parameter.
I don't understand what the second limitation means. The current
design draft has no way to require that a type parameter have an
interface type. Can you give an example of what you mean?
> Otherwise, we would break the substitution property: the meaning of a type-list interface would depend upon whether it was written literally in the source code or passed as a type argument. (That is: the meaning of a generic declaration instantiated with a given list of argument types would be different from the meaning of the same declaration with some of its actual arguments substituted for the corresponding parameters.)
Again I'm sure that I don't understand what you are saying. My
initial reaction is that of course the meaning of a type-list
interface depends on whether it is used as a type constraint or is
passed as a type argument. Those are two entirely different
operations. You can't use a type parameter as a type constraint. I
suppose you could regard that as a limitation of the system, but it's
an intentional one. If a type parameter can be a type constraint,
then there is no contract for the generic function: it has ceded
control over what type arguments are permitted. It is no longer
possible to compile the generic function separately. It's a very
different semantic model.
> I think both of those limitations are severe.
>
> If a type-list interface cannot be used as a type argument, then type-list interfaces are not really “interface types” — in fact, they are not “types” at all, because they cannot be used as even unrestricted type arguments.
> On the other hand, if parameters of interface type cannot be used as constraints for other type parameters, then the constraints are not really “interface types” in general, but rather something more restricted. (In the current draft we have no explicit way to express that a parameter is “of interface type” in general, but it doesn't seem like a stretch to make parameters-as-subtyping-constraints work in general.)
I don't know if I would say that they are something more restricted,
exactly, but using them in a different way has a different effect.
> To me, this all suggests that the “underlying type” sort of constraint should be conceptually and syntactically different from the “simply implementing the interface type” sort of constraint.
I don't necessarily object, if we can find the right name and syntax,
but I don't understand how that would be effectively different from
what the design draft says today. We would say that a type constraint
can be an interface type, or it can be this other thing that is an
interface type plus a list of types.
Ian