-
Notifications
You must be signed in to change notification settings - Fork 566
Type as Arguments (based on reflect.Type)
This proposal adds Type as Arguments syntax to XGo, allowing bare type expressions to be passed where a reflect.Type parameter is expected. The compiler automatically rewrites each type expression to reflect.TypeFor[T](), eliminating boilerplate without any runtime changes.
Since Go 1.22, the idiomatic way to obtain a reflect.Type is reflect.TypeFor[T](). While cleaner than the old reflect.TypeOf((*T)(nil)).Elem() idiom, it still clutters call sites — especially with multiple types:
foo(reflect.TypeFor[int]())
accept(reflect.TypeFor[A](), reflect.TypeFor[B](), reflect.TypeFor[C]())The type names are the only semantic content; the wrappers are pure ceremony. XGo should let developers express the same intent directly:
foo int
accept A, B, C
When a parameter is declared as reflect.Type or ...reflect.Type, callers may pass bare type expressions at that position:
// Single reflect.Type parameter
foo int
foo MyStruct
foo *http.Request
// Variadic ...reflect.Type parameter
accept A, B, C
The feature activates when the callee declares a parameter of type reflect.Type (from the standard library):
// Single
func foo(t reflect.Type)
// Variadic
func accept(typs ...reflect.Type)-
reflect.Typeparameters only. The feature applies to parameters declared asreflect.Type. It does not apply tointerface{},any, or user-defined aliases ofreflect.Type. -
Any valid type expression. Named types, pointer types, slice types, map types, generic instantiations, and so on are all accepted.
-
Variadic expansion. For
...reflect.Type, a comma-separated list of type expressions is accepted; each is rewritten independently. -
Mixed argument lists. Non-
reflect.Typeparameters continue to accept ordinary value expressions unchanged.
Each bare type expression T at a reflect.Type argument position is lowered to:
reflect.TypeFor[T]()The generated Go code is standard and requires no runtime changes.
| XGo | Generated Go |
|---|---|
foo int |
foo(reflect.TypeFor[int]()) |
foo *http.Request |
foo(reflect.TypeFor[*http.Request]()) |
accept A, B, C |
accept(reflect.TypeFor[A](), reflect.TypeFor[B](), reflect.TypeFor[C]()) |
setHandler "events", MyEvent |
setHandler("events", reflect.TypeFor[MyEvent]()) |
schema.Add Table, Index, View |
schema.Add(reflect.TypeFor[Table](), reflect.TypeFor[Index](), reflect.TypeFor[View]()) |
At a reflect.Type argument position, XGo first tries to resolve the argument as a type. If it resolves to a type in the current scope, it is rewritten to reflect.TypeFor[T](). Otherwise, it is treated as a value expression of type reflect.Type (e.g., a variable).
foo t // t is a reflect.Type variable — passed as-is
foo int // int is a type — rewritten to foo(reflect.TypeFor[int]())
foo MyStruct // MyStruct is a type — rewritten to foo(reflect.TypeFor[MyStruct]())
This is a purely additive change. Existing calls using explicit reflect.TypeFor[T]() remain valid. The new syntax is only available in XGo source files (.xgo / .gox); generated Go files always use the explicit form.
Type as Arguments eliminates the most common source of verbosity in reflection-heavy Go code. The transformation is mechanical, the output is standard Go, and existing code is entirely unaffected.