Resolving type parameter ambiguities

August 2, 2023

First today, one rather mundane sentence from the spec…

Type parameter declarations

Just as each ordinary function parameter has a parameter type, each type parameter has a corresponding (meta-)type which is called its type constraint.

Followed by a bit of WTF…

A parsing ambiguity arises when the type parameter list for a generic type declares a single type parameter P with a constraint C such that the text P C forms a valid expression:

type T[P *C] …
type T[P (C)] …
type T[P *C|Q] …
…

If you’re up for it, I’ll try to break this thing down. If you’re not up for it, no worries. See you tomorrow! 👋😆

Let’s consider the first example provided: type T[P *C] …. How can this be ambiguous?

Well, it looks like an array type declaration, where the array length is derrived from a constant expression, such as in this example:

const P = 3
const C = 4

type T [P * C]int // T is of type [12]int

Other expressions are possible, too, which is what leads to this possible ambiguity.

So, we need a way to solve this WTF scenario, and Go gives us two of them…

In these rare cases, the type parameter list is indistinguishable from an expression and the type declaration is parsed as an array type declaration. To resolve the ambiguity, embed the constraint in an interface or use a trailing comma:

type T[P interface{*C}] …
type T[P *C,] …

So either wrap your constraint in interface{…}, or add a magical comma at the end. Poof! problem solved.

But still. WTF?!

Quotes from The Go Programming Language Specification Version of December 15, 2022


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


Switch expressions

Expression switches … If the switch expression evaluates to an untyped constant, it is first implicitly converted to its default type. The predeclared untyped value nil cannot be used as a switch expression. The switch expression type must be comparable. In this paragraph, “switch expression” refers to the expression that optionally comes after the switch keyword, as in: switch len(x) { So first off, if it’s a untyped constant, it’s first converted to its default type.


Conversion of type parameters

Today I’ll be live-streaming again, doing TDD on an open-source project. I hope you can join! Conversions … Additionally, if T or x’s type V are type parameters, x can also be converted to type T if one of the following conditions applies: Both V and T are type parameters and a value of each type in V’s type set can be converted to each type in T’s type set. Only V is a type parameter and a value of each type in V’s type set can be converted to T.


Exact & loose type unification

Type unification … Unification uses a combination of exact and loose unification depending on whether two types have to be identical, assignment-compatible, or only structurally equal. The respective type unification rules are spelled out in detail in the Appendix. The precise definitions of “exact” and “loose” unification are buried in the appendix, and depend on the specific types involved. In general, I think it’s not terribly inaccurate to say that exact unification applies when the two types are identical, for composite types with identical structure (i.