Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.Sign up
proposal: Go 2: Add new type designed to aggregate small interfaces #29467
I have written up a proposal for Contracts in Go on my new blog.
I wrote it there before realizing others have written their proposals inline on Github issues. If requested I will be happy to do the extra work to copy the content over and format it correctly for markdown, but if that is not needed it would be great so I don't have to go to that effort.
Just let me know please.
This is interesting, but I think you'll definitely need a different name for what you're proposing, since 'contract' will mislead everyone to assume this is about generics.
I agree that large interfaces (like
I certainly think the use of small interfaces, composed into larger ones where necessary, should be encouraged. But in general the design philosophy of Go seems to be to encourage desired behaviour by taking away alternatives, not by providing new ones.
If I'm reading the proposal correctly, contracts are essentially the same as interfaces. I personally don't find you rebuttal at the end to be convincing: just giving the type a different name doesn't make it usefully different.
The idea of an optional method is interesting, but you don't really explain how that works. I assume that it means that you can convert a type to the contract even if it doesn't implement the method, but what happens if the method is called?
@bitfield Thanks for your comments
When I saw that I used the same name as has been proposed for generics, I asked myself which concept makes better use of the name contract, and I honestly and objectively think that my proposal is more inline with how programmers have been speaking of "contracts" for the past decade or more.
Further I would argue, and if I have plans to I will argue this in a blog post (I have a large critical finish a project first), that what the Go team is calling a
The differences are:
Can you elaborate on how you think optional would make things worse? Remember, I proposed is so that backwards compatible additions could be made to an interface and not break code code in the wild that uses prior versions of the interface.
And with your elaboration, maybe a proposal for an alternate that could address how to allow an interface to evolve in a backward compatible manner without breaking code in the wild?
That I am aware of, and actually support in general. Except I would not call this an alternative; in Go2 this could actually be the only option for multi-method interfaces, if the Go team so chose to do so.
Also I would ask the rhetorical question, is the design philosophy motivated by a desire to keep things from being added? Or is it motivated to keep the language easy to reason about where developer's intentions are able to be very clearly indicated in code? If the former, then ok.
But if the latter I would argue adding
Thank you so much for taking the time to comment.
As I envision them,
The two really do address different use-cases. With
Hopefully you find my above explanation more convincing? :-)
The same thing that happens when you assert a type on an interface; it would
For optional methods the caller would be responsible in advance to verify that the method exists before calling it. And being marked as optional, the developer would be able to see that it is their responsibility, and I don't know enough about compilers to know for sure but I would assume that compiler could verify if it had first been checked before being called.
Another option could be that all optional methods could return the zero type if they do not exist but that they could be called like this for the developer to know if they existed or not:
Also, regardless of your decision on this proposal may you please allow me I suggest you consider renaming your proposed feature for generics to "constraints" instead of contracts?
I think what you have proposed feels more like a constraint than a contract, or at least it is easier to reason about as a constraint than as a contract, at least to me.
There is also the fact people have been referring to interfaces as contracts for decades, so I think that calling them contracts might cause confusion for new people coming to go. #JMTCW
In the generics design draft we chose to use the word contract because the contract defines the relationship between two different parts of the code: the function with type parameters, and the type arguments used to instantiated that function. Also, arguably, a contract is a collection of constraints, rather than a single constraint. I agree that the word "contract" is also used in different ways in other parts of the programming world. I think that's OK.
As far as this proposal goes, it's OK when a single concept serves multiple purposes. Personally I don't think it aids comprehensibility to give the multiple purposes different names. We already get plenty of discussion about the difference between
@ianlancetaylor Understood, and I appreciate the consideration you gave this proposal.
However, there were two (2) aspect of this it seems that your consideration did not address when you closed this ticket which were actually the motivation for requesting
(Should I create a new ticket for these instead of discussing on this closed ticket?)
Also, #2 can be done in the current language with interfaces. If the optional method is
Then at a use point, you can do
Hi @randall77 - Thanks so much for the super quick response.
Thank you for that; doing that had not occurred to me.
However it only addresses the issue on selected use-cases but not all, see below for more.
Also, it is not as discoverable as an explicit keyword, as evidenced by the fact I've been lamenting the need for this for months, I've googled for it to no avail, and had to make a feature request to discover it.
If we had the following it would be more discoverable for a programmer new to Go:
Yes, thank you. I have been using this technique as a fall back for what I was asking for.
But it does not actually address the use-case for which I proposed a new type designed to aggregate small interfaces. And that use-case could be addressed by extending interfaces (I still think a new type would be more elegant as a new concept, but accept that that is not in the cards.)
Your example, repeated here, addresses the use-case for a single-method interface being optional,
Here, a more fleshed-out example showing a multi-method interface which is the use case
Here is what I have been doing to workaround the lack of optional methods in interfaces in Go:
A key problem with this workaround is nowhere in the code can we declare to the compiler or an IDE, including with your idiomatic suggestion, that any use of interface
Yes they could be commented but not in a manner that an IDE could recognize and flag or the compiler could validate.
Here is what I propose instead:
Also, the above is easier to read and three (3) lines of calling code instead of nine (9).
Plus, as a side bonus, 15 fewer presses of the shift key meaning my wrist would be thankful
I hope this clarifies?
As an aside, here is why I still lament the lack of interest in a new type designed to aggregate small interfaces.
Let us assume we have a different type than
Go has a high bar for adding new features to the language. That bar is especially high when there's already a way within the language to do what a new feature would do. Even more so when the way to do it is a one-liner.
Your point about discoverability is a good one, though. We should collect nuggets of knowledge like this somewhere. Maybe in the wiki?
I'm confused by this claim. If
Or is that not what you mean?
I can appreciate that, and is one of the reason I generally really like Go as a language.
Which is why anything I might propose I first would want to see it simplify the code a developer would need to write and that other developers would need to read *and make written code more clear to a reader, and I think my proposal reaches that bar, no?
Yes, that is possible, but it does not explicitly define an association between
Let's discuss this with more concrete class name; using abstract names like
Let's assume I want to implement a "connectors" (my word) for an Email Service Provider (ESP.) I want to provide connectors for SendMail, Amazon SES, and Mailjet but leave the door open to add connectors for Postmark or SendInBlue in the future (or to let someone else write those.)
Further, let's assume that while these services are similar there are differences in these services. Let's assume some offer delivery receipts and others do not. Some allow sending in batch, some only allow sending one at a time, and potentially a lot more differences.
What I would like to do is define an interface named
Given your proposed solution I would need to define an
It is also more complexity for my implementing code and for usage code, and as people already complain about how required error handling makes Go code more complex than it needs to be it would seem to me that adding
All changes make things better in some way. Nobody proposes useless changes.
But all changes also have costs. Adding anything to the language carries a heavy cost: every user of the language has to learn the new concept.
When considering any language change, we must consider not only the benefits, we must also consider the costs. Does the change bring enough new value to justify the cost of making everybody learn about it?
See also https://blog.golang.org/go2-here-we-come for more discussion about when to make a change to the language.
@ianlancetaylor Thanks on your link. Based on your three criteria:
With your blessing I would like to add a new proposal for optional methods to see if there are indeed other Go developers for whom it would address their needs enough that it could be indentified as "many?"
That's exactly what interface embedding is for. Embed
Ah, okay. Then I guess the remaining issue is the difference between having to write all this code:
vs. just being able to write this: