[generics] Interface as a contract criticism

134 views
Skip to first unread message

lx.g...@gmail.com

unread,
Jul 4, 2020, 3:06:22 PM7/4/20
to golan...@googlegroups.com
Hey! The new draft is way better than previous, but it still allows to write some tricky code.

func String(type T fmt.Stringer) (x T) string {
return x.String()
}

This is not really a good type parametrization example. Why to have such a feature? What is the point? What is the difference between it and this code:

func String(x fmt.Stringer) string {
return x.String()
}

I believe it must be not allowed to use specific types in parametrization. Here is one more example:

func Add(type T int) (x, y T) T {
return x + y
}

This doesn't make any sense.

I think it must be allowed only to use "contracts" as a parametrizing type like this:

type MyContract interface{
type int, string
}

And it must be denied to use such a contract:

type MyContract interface{
type int
}

Since it doesn't really make sense at all, too. It is only a way to clutter everything up, and it is not a useful feature.

The second issue is interfaces must serve only on purpose - matching a struct with some methods. I believe "contracts" must be a separate feature. And my proposed syntax is:

type MyContract int | string

Another option is to implement it like this:

type MyContract interface int, string

So, it would not allow specifying any methods and could be treated as a "contract" or "list type" as I call it. It also has way simpler syntax.

It is a way simpler thing, serves exactly one purpose, doesn't confuse and allows to handle a parametrized types easier.

// Only a "contract" is allowed as a parametrizing type
func Add(type T int | string) ... // direct
func Add(type T MyContract) ... // named

Another feature I would like to propose is a type checker improvement, so it would allow to access fields of parametrizing types. So You could wirite:

var x T
s := x.Name

I have created my own demo for it: https://playgolang.hulakengineering.com (click "NAME FUNCTION EXAMPLE")

Thank You for the attention, I hope my proposals will be considered.

Ian Lance Taylor

unread,
Jul 4, 2020, 3:31:41 PM7/4/20
to lx.g...@gmail.com, golang-nuts
On Sat, Jul 4, 2020 at 12:06 PM <lx.g...@gmail.com> wrote:
>
> Hey! The new draft is way better than previous, but it still allows to write some tricky code.
>
> func String(type T fmt.Stringer) (x T) string {
> return x.String()
> }
>
> This is not really a good type parametrization example. Why to have such a feature? What is the point? What is the difference between it and this code:
>
> func String(x fmt.Stringer) string {
> return x.String()
> }

I agree that for that case there is no significant difference. That's
why the design draft examples are all presented in terms of []T.
There is a significant different between []T where T implements
fmt.Stringer and []fmt.Stringer. From the general, non-generic, Go
FAQ: https://golang.org/doc/faq#convert_slice_of_interface .


> I believe it must be not allowed to use specific types in parametrization. Here is one more example:
>
> func Add(type T int) (x, y T) T {
> return x + y
> }
>
> This doesn't make any sense.

For what it's worth, the current design draft also doesn't permit it.


> I think it must be allowed only to use "contracts" as a parametrizing type like this:
>
> type MyContract interface{
> type int, string
> }
>
> And it must be denied to use such a contract:
>
> type MyContract interface{
> type int
> }
>
> Since it doesn't really make sense at all, too. It is only a way to clutter everything up, and it is not a useful feature.

Sorry, I'm not sure quite what you are suggesting. Do you mean that
we should forbid a type list with only one entry? I agree that such a
type list is not obviously useful, but it would be strange to forbid
it entirely. We shouldn't add arbitrary restrictions to the language
just because we don't see why anyone would want to use them.


> The second issue is interfaces must serve only on purpose - matching a struct with some methods. I believe "contracts" must be a separate feature. And my proposed syntax is:
>
> type MyContract int | string
>
> Another option is to implement it like this:
>
> type MyContract interface int, string
>
> So, it would not allow specifying any methods and could be treated as a "contract" or "list type" as I call it. It also has way simpler syntax.
>
> It is a way simpler thing, serves exactly one purpose, doesn't confuse and allows to handle a parametrized types easier.
>
> // Only a "contract" is allowed as a parametrizing type
> func Add(type T int | string) ... // direct
> func Add(type T MyContract) ... // named

It's plausible. My only comment here is that we would need to
carefully the meaning of "type MyContract int | string". If we permit
that, then we are adding a new kind of type to Go's type system, a sum
type. We need to define exactly how those work, including defining
what the zero value is. Of course we could say that such types can
only be used as type constraints, but that seems like a somewhat
arbitrary restriction.


> Another feature I would like to propose is a type checker improvement, so it would allow to access fields of parametrizing types. So You could wirite:
>
> var x T
> s := x.Name
>
> I have created my own demo for it: https://playgolang.hulakengineering.com (click "NAME FUNCTION EXAMPLE")

This sounds similar to the discussion at
https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md#notes-on-composite-types-in-type-lists
.

Thanks for the note.

Ian

lx.g...@gmail.com

unread,
Jul 4, 2020, 5:51:13 PM7/4/20
to golang-nuts
Both

type X int | string

and

type X interface int, string

Are meant to be a syntax sugar for:

type X interface {
 type
int, string
}

It is not a sum type, but rather a generic type that needs to be instantiated before the use. That is why it cannot have a zero value:

var x X // error, X must be instantiated before the use

But i think one should not be able to write:

type X interface {
 type
int
 
String() string
}

But instead it must be something like this:

type X interface {
 type
int, fmt.Stringer
}

Or:

type X interface int, fmt.Stringer

Or:

type X interface int, interface{String() string}

https://go2goplay.golang.org/p/fNqNeDyM3R9

I do understand that this is kinda the same case like x.Name (complex literals in contracts), but in my opinion it would bit a bit more consistent syntax, wouldn't it?

Steven Blenkinsop

unread,
Jul 4, 2020, 10:36:23 PM7/4/20
to lx.g...@gmail.com, golang-nuts

Not really more consistent. Consider:

type I interface {
    type A, B
    C
}

The constraint this represents could be logically expressed as (type A | type B) & (interface C), where type constraints require the underlying types to match, and interface constraints require the interface to be satisfied. By writing this as interface A, B, C, you're erasing both the distinction between a type constraint and an interface constraint, as well as the distinction between X & Y and X | Y. That would make your alternative syntax inconsistent to my mind.

lx.g...@gmail.com

unread,
Jul 5, 2020, 8:23:48 AM7/5/20
to golang-nuts
It all sounds now reasonable. Thanks for the attention. Good job!
Reply all
Reply to author
Forward
0 new messages