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 withgotypesalias=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.
Comments on this page:
|
|