I opened an issue to start a discussion around this, but perhaps it was the wrong place, and it is better to begin with email- though formatting seems quite a bit worse over email.
https://github.com/golang/go/issues/74916 Here is the relevant contents of that issue: Preface This topic has almost certainly been covered elsewhere; if that is the case, please direct me to the appropriate discussion and I'll read through it. I did try looking for something that covers this explicitly, and while I found somewhat similar discussions (such as those around creating discriminated unions), I didn't find this exact one. Overview TLDR; Disallowing the use of uninstantiated types as function parameters reduces clarity, is surprising, and leads to bugs. Allowing them improves communication within the codebase. Definition of instantiation, from https://go.dev/blog/intro-generics#type-parameters Providing the type argument to GMin, in this case int, is called instantiation. Instantiation happens in two steps. First, the compiler substitutes all type arguments for their respective type parameters throughout the generic function or type. Second, the compiler verifies that each type argument satisfies the respective constraint. This process is required before a type may be used in a function definition. This restricts the ability of the programmer to communicate effectively the behaviors of their functions, and makes the use of generics as function argument types an all-or-nothing proposition: either the function must be "concrete"- that is, instantiated- or any must be used. Example Take the following example: package main import "fmt" type Test[T any] interface { Get() T } type TestImpl[T any] struct { value T } func (t *TestImpl[T]) Get() T { return t.value } // This is the problem func Print(t Test) { switch t := t.(type) { case Test[string]: fmt.Println(t.Get()) default: fmt.Println("not a supported type") } } func main() { Print(&TestImpl[string]{value: "test"}) } This will not compile, resulting in cmd/types/main.go:17:14: cannot use generic type Test[T any] without instantiation. Instead, the programmer is left with 2 options: Option 1 The programmer may "color" the print function (if you're unfamiliar, it's the concept presented here <https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/> but applied more generally). func Print[T](t Test[T]) { // .... } This doesn't really "solve" anything, it means the caller is now faced with the same problem. Worse, if the Print function is attached to a struct, the struct must also be genericized, and the lack of union types means that it can't necessarily be done properly. Consider a struct that can print Test things: type Printer[T] struct {} func (p *Printer[T]) Print(t Test[T]) { fmt.Println(t.Get()) } func main() { stringPrinter := Printer[string]{} stringPrinter.Print(&TestImpl[string]{value: "test"}) intPrinter := Printer[int]{} intPrinter.Print(&TestImpl[int]{value: 1}) } Suddenly we need to create a new printer for every type. If the purpose of printer is to print Test things, this approach does not work. Option 2 Use any as the type parameter. func Print(t any) { switch t := t.(type) { case Test[string]: fmt.Println(t.Get()) default: fmt.Println("not a supported type") } } func main() { Print(&TestImpl[string]{value: "test"}) } This is the common approach. The issues is that the any type communicates nothing, and provides no compiler guarantees. The programmer cannot communicate to the caller the expectations of the function, or even the approximate expectations. Proposal If the compiler allowed the use of uninstantiated types as function parameters, the programmer could better communicate the expectations of the function to the caller. func Print(t Test) { switch t := t.(type) { case Test[string]: fmt.Println(t.Get()) default: fmt.Println("not a supported type") } } This would be backwards compatible since today it would simply result in a compile error. Preemptive objections and responses *The function parameter still contains no concrete type information, so it is not usable by the function code.* This is true; the programmer would need to use a type assertion to get the instantiated type. In that way, it behaves like any does today. However, the compiler is still more effectively able to bound the inputs. For example, the compiler could error like it does with other impossible type assertions: type I interface { Get() string } // This is a compiler error in Go func Coerce(t I) { t2 := t.(string) // impossible type assertion: t.(string) // string does not implement I (missing method Get) } type Test[T any] interface { Get() T } // This could be a compiler error func Coerce2(t Test) { t2 := t.(string) // impossible type assertion: t.(string) // string is not of type Test } *This wouldn't work on structs, since type assertions are only allowed on interfaces* Even if the compiler only allowed uninstantiated types for interface arguments, this would still be better than just using any today. It is also not clear to me that this definitely wouldn't be possible for structs. In a sense, an uninstantiated generic struct is like an interface, so there might be a reasonable way to implement it, something like creating implicit interfaces for generic structs. Certainly, from a language perspective, it seems like it would be reasonable to allow uninstantiated struct types. *The programmer still needs to type assert within the function, and it may be non-exhaustive* This is no different from using any today, but still communicates more to the caller and allows additional compiler checks. The ability to communicate more precisely add clarity with no additional mental cost and is a good thing. Conclusion This seems to me like a useful, backwards compatible change. Because it seems so useful, and because the Go team tends to think very carefully about the language, I think I'm probably missing something. So I would appreciate feedback on this proposal, including any previous proposals that I may have missed and of course issues with doing this. ----- Thanks, John Mark Wilkinson -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/5393423d-7441-4b9a-a020-4c00eae40e66n%40googlegroups.com.