The Proposal

The design draft for generics in Go 2 is published for some days now and it’s no surprise given the immense amount of history that has taken place. Go already supports type switches and interfaces that enable very loose generic code, but still some kind of generic behavior is desired enough to bring about this proposal.

I intend to demonstrate that the draft for contracts proposes an unfit solution to generics on the grounds that it breaks several key benefits of the language for little benefit.

Highly Variable Function Signtures

func Print(type T)(s []T) {
}

Above is a simple example of the proposed new set of optional parameters that are used to declare the use of a contract. The signature of a func in Go is currently easy to read due to the careful placement of optional items. With this new optional parameter, you can no longer visually use the position of signature elements and must carefully identify the lengthy type parameter to know what this signature says.

Here are 3 examples of function signatures. Can you tell which one is using a contract easily?


func One(x string, b bool)(err error) {
}

func Three(x string)(b bool, err error) {
}

func Two(type T)(x string, y T) {
}

Now imgine you are a new programmer. All the optional parts of a signature are appearing and disappearing between functions. That would be really confusing and heavily goes against Go being “easy to learn”.

Too Similar to Interfaces

Here is a quote from the design draft:

“In this design, a contract describes the requirements of a set of types. We‘ll discuss contracts further later, but for now we’ll just say that one of the things that a contract can do is specify that a type argument must implement a particular method.”

This is far too close to an interface. When do you use an interface? When do you use a contract? The lines are too blurry, even after you put the effort in to learn what they are.

Even worse, unlike interfaces - contracts are very static. You must explicitly define them, but you must ALSO explicitly USE them. This would be a first in the language and will undoubtedly trigger massive refactors when used in large codebases for extended periods of time. So then, it seems that contracts suffer from the same drawbacks that explicit interface implementing in Java does.

This was one of the major benefits to interfaces in Go! You don’t explicitly “implement” interfaces and thus enable higher code re-use with less overhead. I feel like the language deliberately avoided things like contracts at the onset and is now playing with abandoning one of its core concepts many people love in order to take a stab at a solution for generics.

Function Signature Bloat

func signatures become much more jumbled and messy with the addition of this new contracts section added in. Big function signatures increse cogntitive overhead and make us worse programmers. Here are some examples of realisticly-sized signatures contracts would bring:

func (u *User) AsString(type T stringer)(s []T, b bool) (ret []string, err error) {}

func (c *Cat) Meow(type T petSoundContract)(cats []*Cat, voice []T) (Sound, error) {}

func (c *Car) Drive(type T1 selfDriving, type T2 autoNavigation)(directions Directions, drivingCPU []T1, navigationCPU []T2) (t *Trip, err error) {}

Let’s not forget that a contract also has a spec that you have to click into and understand. Now try thinking about some situations that implement a contract AND satisfy an interface. No, thanks.

Crowding of the Namespace

The Go namespace for all base level functions, structs, variables, and interfaces is already rather croweded. Go only has packages to divide all of these things. To help with this, a couple naming conventions emerged that let you identify what things are at first glance:

  • strings and errors are packages. string and error are types. Without proper IDE hilighting, this is invisible to the user. Adding an s normally means a package reference.

  • stringer and io.Writer are interfaces. Interfaces normally end in an er.

This means that the error type does not collide with the errors package and so on. Are we sure we want to add contracts into this arena? Even with all those func signature changes??

The Proposal is Huge

This proposal for this change is LONG - coming in at 14,561 words. Even if you could read continuously without thinking about the examples, it would take the average college student 32.5 minutes to get through the proposal. As a comparison, RFC2460 (IPv6) is just 9,392 words and it explains the implementation of a worldwide IP protocol.

One of my favorite quotes from Einstein comes to mind here:

If you can’t explain it simply, you don’t understand it well enough.

Honestly, I stopped reading the proposal word for word about half of the way through when new exceptions and rules were still coming. I think the size of this document shows that the implementation of contracts is nuanced and in my opinion, user-hostile.

Go has always taken the approach of making small changes and waiting for the next steps to become clear. That does not feel like what we are doing here.

Closing

For the reasons above and more, it is my opinion that contracts are seldom the best way to solve an issue. When they are, the extra code to define and implement contracts (very non-flexible code) isn’t better than just implementing a blank interface with a type switch.

I think it would be much more productive if the proponents came up with some code examples that they think would be best solved by implementing contracts. With an example in hand, we could see how bad it would be if solved with the existing interfaces in the language today and we could evaluate from there. I didn’t see this kind of thought exercise anywhere in those 14.5k lines.

In closing, we as the Go community should be very careful not to accidentially listen to the loud minority, even if they at first glance appear to be the majority. Go offers a lot of ways to solve coding problems already and we shouldn’t add anything unless we’re fixing a really large shortcoming. Even in that situation, we should be very careful with the tradeoffs in complexity that come with.

« Back to Article List

Comments

comments powered by Disqus