Wandering Thoughts archives


Go 1.22's go/types Alias type shows the challenge of API compatibility

Go famously promises backward compatibility to the first release of Go and pretty much delivers on that (although the tools used to build Go programs have changed). Thus, one may be a bit surprised to read the following about go/types in the Go 1.22 Release Notes:

The new Alias type represents type aliases. Previously, type aliases were not represented explicitly, so a reference to a type alias was equivalent to spelling out the aliased type, and the name of the alias was lost. [...]

Because Alias types may break existing type switches that do not know to check for them, this functionality is controlled by a GODEBUG field named gotypesalias. [...] Clients of go/types are urged to adjust their code as soon as possible to work with gotypesalias=1 to eliminate problems early.

(The bold emphasis is mine, while the italics are from the release notes. The current default is gotypesalias=0.)

A variety of things in go/types return a Type, which is an interface type that 'represents a type of Go'. Well, more specifically these things return values of type Type, and these values have various underlying concrete types. Some code using go/types and dealing with Type values can handle them purely as interfaces, but other code needs to specifically handle all of the particular types (such as Array and so on). Since Type is an interface, such code will use a type switch that is supposed to be exhaustive over all of the concrete types of Type interface values.

Now we can see the problem. When Go introduces a new concrete type that can be returned as a Type value, those previously exhaustive type switches stop being exhaustive; there's a new concrete type that they're not prepared to handle. This could cause various problems in actual code. And Go has no way of requiring type switches to be exhaustive, so such code would still build fine but malfunction at runtime.

Much like the last time we saw something like this, this change is arguably not an API break, at least in theory; Go never explicitly promised that there was a specific and limited list of go/types types that implemented Type, and so in theory Go is free to expand the list. However, as we can see from the release notes (and the current behavior of not generating these new Alias types by default), the Go authors recognize that this is in practice a compatibility break, one that they're explicitly urging people to be prepared for.

What this shows is that true long term backward compatibility is very hard, and it's especially hard in an area that is inherently evolving, like exposing information about an evolving language. Getting complete backward compatibility requires more or less everything about an exposed API to be frozen, and that generally requires the area to be extremely well understood (and often pushes towards exposing very minimal APIs, which has its own problems).

As a side note, I think that Go is handling this change quite well. They've added the type to go/types so that people can add it to their own code (which will make it require Go 1.22 or later), and also provided a way that people can test the code (by building with gotypesalias=1). At the same time no actual 'Alias' types will appear (by default) until some time in the future; I'd guess no earlier than Go 1.24, a year from now.

programming/Go122TypesAliasAndCompatibility written at 21:31:08; Add Comment

Page tools: See As Normal.
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.