Go 2 Generics: The usefulness of requiring minimal contracts

April 22, 2019

I was recently reading Ole Bulbuk's Why Go Contracts Are A Bad Idea In The Light Of A Changing Go Community (via). One of the things that it suggests is that people will write generics contracts in a very brute force fashion by copying and pasting as much of their existing function bodies into the contract's body as possible (the article's author provides some examples of how people might do this). As much as the idea makes me cringe, I have to admit that I can see how and why it might happen; as Ole Bulbuk notes, it's the easiest way for a pragmatic programmer to work. However, I believe that it's possible to avoid this, and to do so in a way that is beneficial to Go and Go programmers in general. To do so, we will need both a carrot and a stick.

The carrot is a program similar to gofmt which rewrites contracts into the accepted canonical minimal form; possibly it should even be part of what 'gofmt -s' does in a Go 2 with generics. Since contracts are so flexible and thus so variable, I feel that rewriting them into a canonical form is generally useful for much the same reasons that gofmt is useful. You don't have to use the canonical form of a contract, but contracts in canonical form will likely be easier to read (if only because everyone will be familiar with it) and easier to compare with each other. Such rewriting is a bit more extreme than gofmt does, since we are going from syntax to semantics and then back to a canonical syntax for the semantics, but I believe it's likely to be possible.

(I think it would be a significant danger sign for contracts if this is not possible or if the community strongly disagrees about what the canonical form for a particular type restriction should be. If we cannot write and accept a gofmt for contracts, something is wrong.)

The stick is that Go 2 should make it a compile time error to include statements in a contract that are not syntactically necessary and that do not add any additional restriction to what types the contract will accept. If you throw in restrict-nothing statements that are copied from a function body and insist that they stay, your contract does not compile. If you want your contract to compile, you run the contract minimizer program and it fixes the problem for you by taking them out. I feel that this is in the same spirit as requiring all imports to be used (and then providing goimports). In general, future people, including your future self, should not have wonder if some statement in a contract was intended to create some type restriction but accidentally didn't, and you didn't notice because your current implementation of the generic code didn't actually require it. Things in contracts should either be meaningful or not present at all.

To be clear here, this is not the same as a contract element that is not used in the current implementation. Those always should be legal, because you always should be able to write a contract that is more strict and more limited than you actually need today. Such a more restrictive contract is like a limited Go interface; it preserves your flexibility to change things later. This is purely about an element of the contract that does not add some extra constraint on the types that the contract accepts.

(You can pretty much always relax the restrictions of an existing contract without breaking API compatibility, because the new looser version will still accept all of the types it used to. Tightening the restrictions is not necessarily API compatible, because the new, more restricted contract may not accept some existing types that people are currently using it with.)

PS: I believe that there should be a gofmt for contracts even if their eventual form is less clever than the first draft proposal, unless the eventual form of contracts is so restricted that there is already only one way to express any particular type restriction.

Comments on this page:

This is a really good idea. I don't know that checking for minimal contracts would be terribly easy to implement, though. (I have the feeling that writing out the contract in the form of a function that can be compiled with the existing compiler allows a trick or two to make easy what would otherwise be hard.)

That said, I don't find Bulbuk's argument terribly convincing. I'm well aware of the problem of copy-and-paste programmers, but in my experience most of them don't deliberately choose to learn new and more difficult language features so they can use them (badly) in place of existing features they already know. (I have seen exceptions to this, though!) I spent the whole article wondering why copy-and-paste programmer wouldn't just use an interface instead of a generic, given that the interface is obvious, probably something he's used before, and does what he needs.

But anyway, I have grave doubts that avoiding generics in Go would help with the cut-and-paste-programmer problem more than it would hinder Go programming in general. Those kinds of limitations, like checked exceptions in Java, strike me as the sort of "software engineering" thing that used to seem like a good idea to me before I learned to program better. As Dijkstra says in EWD 1036:

A number of these phenomena have been bundled under the name "Software Engineering". As economics is known as "The Miserable Science", software engineering should be known as "The Doomed Discipline", doomed because it cannot even approach its goal since its goal is self-contradictory. Software engineering, of course, presents itself as another worthy cause, but that is eyewash: if you carefully read its literature and analyse what its devotees actually do, you will discover that software engineering has accepted as its charter "How to program if you cannot."

BTW, the comments in something linked from one of the things you linked to lead to a rather interesting paper: "A Comparative Study of Language Support for Generic Programming".

Written on 22 April 2019.
« You might as well get an x86 CPU now, despite Meltdown and its friends
The appeal of using plain HTML pages »

Page tools: View Source, View Normal, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Mon Apr 22 22:13:08 2019
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.