Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.Sign up
proposal: go 2: a simpler approach to generics with "concrete" interfaces #33627
A summary is available at the end for the less patient of you!
The problem with contracts
Much constructive criticism has already been expressed against contracts. Here is a brief list of good arguments I've read:
This is exactly what I am going to present next: an extended, generic version of interfaces.
A case study
Let's say we want to write a generic
It is ambiguous whether
See how the position of
It eliminates the cognitive burden of using
But this is not all, it also enables two possible additions to the language in the future that would then fit naturally:
Now back to our example. We want to indicate that
This is actually wrong: we don't know that
It only makes sense when you think of it with other interfaces:
I call this a "concrete" interface (hence the title): it specifically refers to a given type in contrast with "normal" interfaces, referring to any type matching their description.
I feel that a number of points need to be addressed before continuing any further:
A first major drawback is that you can't specify fields with an interface, which is a good design decision in my opinion. I assume that the initial philosophy was that when exporting an API, only how you can interact with an object matters, not its inner working. Contracts, on the other hand, allow exposing the internals to the users, and may restrict how a structure is implemented.
If yet, such a possibility is desired, we could naively think at first of some way using generic structures. Let's say, for a new example, that we want to write a generic
Following the same logic than with interfaces, we would think of something similar to this:
... This time,
At this point, there are two solutions:
I strongly support this second option. Since interfaces containing field requirements cannot be exported, there is good practice to write separate, unexported interfaces containing field requirements only. It would still be very useful inside of a package to indicate constraints, and would prevent exposing the internals of the package when used by someone else.
The second drawback is what probably motivated the idea of contracts in the first place: operators. In our example with
Whether contracts are implemented or not in the end, code duplication will still occur with primitive types. Since composite types are more frequent than primitive types, a generic function will most likely end up using methods rather than operators; therefore using methods should be preferred. Given this perspective, we should try to find a suitable solution with interfaces one more time, using what the language already permits.
As stated before in the preliminary criticisms of contracts, some operators are implicitly allowed (
As the name suggests, they can be used like interfaces:
A number of derived interfaces could be predeclared as well:
With a set of sane, predeclared pseudo-interfaces, developers would only need to refer to the standard Go documentation to find out what they mean, instead of tediously trying to guess what a contract implies:
A small issue arises when you think about how to write a generic
Of course, it works. But we'd rather write the function this way:
I suggest we might as well just do that: pseudo-interfaces (and them only) can also be used as "types" of types as it was referred to earlier:
At last, we still have the problem that every generic function will have to be written twice: one for the primitive types, one for the composite types. As a convenience, and for performance reasons, I would encourage the introduction of a new "pseudo-"package named
This way we can write a definitive, generic
Using dark magic with the compiler, we could natively translate
If methods from
That is, if type
EDIT. Just to be clear,
What do you all think?
Disclaimer: I read only summary section, keep that in mind.
Overall this looks really interesting except few things
@target-san I'm not exactly sure to understand what you mean, but "concrete" was just a word I came up with to describe the property of a generic interface to be associated to one of its type parameters. How it translates into an actual implementation is still to be worked on. In particular, it would be nice to be able to write
EDIT. More on concrete interfaces: I see that I wrote they "should be read in code as an assertion on a type", but this is just a hint in how the programmer should think when they see one, not an actual rule on how they should be implemented. It's easier to remember that
On the "interface fields" part I anticipated it would be hard to accept. This is why I suggested forbidding to export interfaces using them in order to sort of keep the original spirit of interfaces (which is in my opinion, as I state in my presentation, a good design decision from the original authors). Or, we could still set this aside and maybe make a decision in a later version of Go 2; and be content with generic structures for now. Maybe this would require a deeper analysis of the existing real-world codebase to decide whether it should be added or not.
Your discussion about contracts appears to refer to the earlier draft. Please take a look at the most recent draft (https://go.googlesource.com/proposal/+/master/design/go2draft-contracts.md) as it has significant differences.
It would help if you could explain how to implement the Graph/Nodes/Edges idea with your suggested syntax. Thanks.