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

February 11, 2024

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.


Comments on this page:

By Joseph at 2024-02-11 23:02:20:

Well, they really want type aliases for the iterator support being added in 1.23. They want to alias seq and seq2. So I expect that break in 1.23. Given how popular iterators will be, I expect people to be upgrade to 1.23 very quickly as well.

I don't see what's so famous about it. The very first sentence is a contradiction to me, describing de jure but not de facto backwards compatibility. Common Lisp and Ada programs still work identically, decades later.

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.

That sounds like backwards incompatibility to me.

And Go has no way of requiring type switches to be exhaustive, so such code would still build fine but malfunction at runtime.

That's amusing to me. I've heard so much about how randomizing iteration order is so clever and important, but then they don't do something simple like this which Ada has been doing since the beginning.

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.

It's not hard in the least. Ada does it just fine. Ada 1995 code will still work just fine, but a man can always choose Ada 2005 or 2012 for their later additions. Clearly, pretending to have just one version of the language is stupid.

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).

Alternatively, it requires careful thought.

By lilydjwg at 2024-02-13 03:16:57:

Getting complete backward compatibility requires more or less everything about an exposed API to be frozen.

Or it requires the language to express incompleteness, like marking an enum to be non-exhaustible---wait, does Go have enum / sum types?

By cks at 2024-02-13 09:20:22:

Go doesn't have true enum / sum types as I understand them, and normal interfaces are explicitly non-exhaustive (in that anyone can make a type that implements an interface). However, I think even an exhaustive enum type and support forcing exhaustiveness on things dealing with it wouldn't truly help here; it would just change a runtime error to a compile time error, not eliminate it.

Written on 11 February 2024.
« My plan for backups of my home machine (as of early 2024)
Linux kernel boot messages and seeing if your AMD system has ECC »

Page tools: View Source, View Normal.
Search:
Login: Password:

Last modified: Sun Feb 11 21:31:08 2024
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.