Description
Rationale
Currently in Go, type inference works in one direction and one direction only: From the values of an assignment to the variable declarations. In other words, given some expression with a statically-determinable type, which is basically every expression, the type of a declaration can be omitted. This allows for a lot of the perceived lightness of Go over many statically typed languages.
However, there are a number of situations where this is not possible, most of which have to do with make()
and new()
, both of which are unique, not including some stuff in the unsafe
package, in that they take a type name as an argument. Normally this is a non-issue, as that type can be used to determine the return of the expression, thus allowing for inference in the normal manner:
m := make(map[string]interface{})
s := make([]string, 0, 10)
p := new(int)
Sometimes the variable must have a separately declared type, though, such as in the case of struct fields:
type Example struct {
m map[string]interface{}
}
func NewExample() *Example {
return &Example{
m: make(map[string]interface{}),
}
}
This leads to unwanted repetition of the type name, making later alteration more awkward. In particular, I thought of this while reading through one of the many proposals about ways to make anonymous structs more useful with channels, and I realized that the following pattern could get very annoying, exacerbating the existing issue:
type Example struct {
c chan struct {
v string
r chan<- int
}
}
func NewExample() *Example {
return &Example{
c: make(chan struct {
v string
r chan<- int
},
}
}
Proposal
I propose the introduction of a new keyword, say typeof
, which takes a variable, function name, or a struct field identifier and essentially acts as a stand-in for a standard type name, possibly with the restriction of only being available as the argument to a make()
or new()
call. For example,
return &Example{
m: make(typeof Example.m),
}
This would allow a type name to be pegged to an already declared variable type elsewhere.
Alternative
Alternatively, make()
and new()
could allow for the aforementioned types of identifiers directly, such as
return &Example{
m: make(Example.m),
}
This has the advantage of being backwards compatible, but is potentially less flexible if one wants to extend the functionality elsewhere later, such as to generics.