-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: spec: extended type inference for make and new #34515
Comments
If we are going to introduce |
Type of |
That's backwards compatible, too, that way, although it brings with it an implicit assumption that Edit: The comment this was a response to seems to have been deleted. Not entirely sure what happened there. It had a suggestion to use |
That's what I want myself. @griesemer points out that you'd also want to do |
Regarding There's clearly a strong sentiment for being able to use |
Building on @dmkra's observation, we could use type Example struct {
m map[string]interface{}
}
func NewExample() *Example {
return &Example{ m: make(_) }
} type Example struct {
c chan struct {
v string
r chan<- int
}
}
func NewExample() *Example {
return &Example{ c: make(_, 1) }
} |
@DeedleFake, there is some precedent for (For example, I used |
I like the Maybe |
Using |
@bradfitz, at least there is precedent for that! The |
Using Another possibility would be var m map[int]string (Using dot might also work for #33359.) But in general this would be a new kind of idea in Go that we don't currently have. |
Or we could take one of the ideas from #33359 and use var m map[int]string
m = make(...) |
Does there need to be anything? Could the absence of a type argument be enough to signal that it should be inferred?
|
Slightly off-topic, but in the case of It's a small nitpick, though. |
Let's definitely keep the fraught discussion of |
There doesn't need to be an argument to |
It would make it somewhat more similar to the recent contracts draft, modulo the number of parens and commas. |
The |
I think If there is any risk that |
Another idea would be to permit referring to the basic kind of type without filling out the elements. For example, var s []byte = make([], 10)
var m map[int]string = make(map)
var m2 map[int]string = make(map, 100)
var c chan bool = make(chan) This might make the code clearer to the reader, while still permitting the omission of the redundant type information. |
The |
Just a few observations:
Of these four approaches, it seems that 1) (just leave the type away) and 4) (write As nice as Thus, notwithstanding some more general concept for |
Even if all other proposals using |
There's also a tangential connection to #12854 in that the contexts in which the type could be deduced are the same for both proposals. |
This proposal was specifically not intended for those cases, as those cases already don't require writing the type twice. This proposal was intended to help only in the cases where the declaration and assignment have to be separate, such as in struct fields. I thought that a way to tell the compiler 'use whatever type this variable was declared as here' would allow for a general solution to it, as the problem stems from the type inference currently only being possible do from value to variable and not the other way around. |
It was pointed out in episode #166 of Go Time that this type of inference would not help in situations where the user was initializing using a composite literal, such as type Example struct {
Values map[string]string
}
// ...
e := Example{
// Still requires a repeat of the type.
Values: map[string]string{
"some": "value",
"or": "another",
},
} That's true. As I said in the original proposal, my primary motivation was channel initialization, though I did think of empty map initialization, too. Slices would be less useful, as appending to a nil slice works just fine. I do still think this would be worth it just for the channel case and the empty map case, though, and, if implemented, it definitely should work for slices too just to be consistent. |
I've found a workaround for some situations when using generics. Although the current generics implementation doesn't have inference for return values, it can do it for pointers: func makeMap[M ~map[K]V, K comparable, V any](m *M, c int) {
*m = make(M, c)
}
type Example struct {
Vals map[string]int
}
var ex Example
makeMap(&ex, 0) I don't know if this has any problematic effects on, for example, optimization, but I recently had a very large number of initialization that I needed to do and each one had a repeated type from a struct definition, so I wrote a function like this and used it instead and it massively cleaned up the code. |
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()
andnew()
, both of which are unique, not including some stuff in theunsafe
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:Sometimes the variable must have a separately declared type, though, such as in the case of struct fields:
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:
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 amake()
ornew()
call. For example,This would allow a type name to be pegged to an already declared variable type elsewhere.
Alternative
Alternatively,
make()
andnew()
could allow for the aforementioned types of identifiers directly, such asThis 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.
The text was updated successfully, but these errors were encountered: