-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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: Go 2: Index on any #63878
Comments
I'd like to check that I'm understanding correctly what you're proposing, by restating it in some different words. The new behavior is that, if the index operator
Is that a correct understanding? If so, I have some follow-up questions:
My initial instinct was that the type assertion is helpful to make it clear that this operation is fallible if the type isn't what's expected, but it's already possible for indexing to panic dynamically in some other situations and so perhaps that is one plausible argument that indexing is "special" compared to some other operations that can be proven not to panic at compile time. Still, this feels quite "un-Go-like", so unless there's some existing precedent in the language that I'm not remembering this feels like a more substantial design decision. |
Rather than a language change, a package seems to be a better fit for these kinds of operations, especially if you want things like defaults or error handling example package: https://pkg.go.dev/github.com/k0sproject/dig |
somewhat related to navigating unknown structures #42847 |
@apparentlymart your understanding is correct. Concerning your first question I think that this proposal is not suitable or applicable for structs because this would nearly completely allow an untyped behavior. This is not the intention. Currently I also don't know something similar in the current language. But nevertheless there are attempts and intentions to make the language more easy. E.g. if anonymous structs are defined currently one has to write struct {... and the definition again. At least for struct slices one can now directly define the values. This is also some kind of an indirect behavior. Concerning your assumption that it makes it more clear that this operation can fail. Yes, at least somehow. But what is the benefit? The result is the same. The error handling should also be the same like for type assertions, i.e. an optional second return that indicates ok or not. |
@seankhliao this is not really related. The proposal you mentioned describes that a nil pointer should be accessible and should not result in a panic. I definitely want that the attempt to access a nil pointer results in a panic. My proposal is only about the type assertion of an any value because the compiler cannot check if the runtime value is indeed what the type assertion implies. This can only be evaluated at runtime. So the result of trying to access a non indexable any value with an index or a type assertion that is incorrect is the same, a runtime error. |
Thanks for the extra detail, @iFrozenPhoenix. The extra part you mentioned about having a two-result form is interesting. v, ok = m["foo"] In today's Go, I think you're suggesting that if You added another comment while I was thinking about your response to me in which you asserted that |
@apparentlymart I think that the proposed behavior should be consistent with the current index access behavior, i.e. if the index value is not found and only one return is expected, then panic. If the value and ok is expected then also the solution in this proposal should not panic, regardless if any is indexable or if the key is not found. Yeah, you're right, the case where someone wants to distinguish between any not indexable and index not found can't be handled due to the fact that ok is a bool and can only represent true or false. The only solution for this would be to return an error but I don't would recommend this and stay compatible with the current behavior of an index access and only return true or false. |
@apparentlymart if someone really needs not know if the reason was that any is not indexable one could use reflection to check if the type is indexable (Slice, Map, String) or not. |
After some further pondering, I did think of one situation in today's Go that you could potentially argue as similar to this proposal: It's valid to compare interface values using I'm not sure I'm very convinced that these two situations are similar enough for this to serve as a precedent, but since I posed the question in the first place I thought I'd at least try to answer it. 😀 |
@bcmills any comment why you don't think the language and the ecosystem could benefit of this? |
It's not consistent with Go's general design as a statically-typed programming language: it would add more run-time failure modes that are not obvious to readers of the code. |
That's an interesting proposal. For more flexible datastructures maybe. For more rigid structures, it's always possible to define a map of map type with it's own getter and setters but obviously the nesting level is fixed. Interesting. |
@bcmills Thank you for your answer. Ok, I haven't expect that. For example if we look at JS or Python, both are largely used, also at Google, and are completely untyped and are capable to be used in large scale projects and result in a reliable runtime behavior. |
@iFrozenPhoenix isn't it possible to define your own function if these are strings? Instead of using the map access operator? func MustGetFrom(obj map[string]any, labels ...string) any{//...}
func GetFrom(obj map[string]any, labels ...string) (any, error) {//...} |
While JS and Python are used in large systems, being dynamically typed leads to many problems for new team members (readability), for refactoring (much harder without types), and general maintenance. These are reasons why Go's dynamic parts of the language are restricted to interfaces. Changing that would fundamentally change the character of the language in ways that are directly counter the original design philosophy of Go. As an aside, much of what you would like to do can be implemented in Go (if need be using reflection) and encapsulated for ease of use. The emoji voting is also not in favor. Therefore, this is a likely decline. Leaving open for four weeks for final comments. |
Ok. Understood. l |
I may not have been clear enough before, so let me state it more explicitly: What you want to achieve can be trivially done with a small helper function, at least if the data structure is represented with maps as in your example (playground): // get implements "dynamic" field access:
// get(m, k1, k2, ... kn) is the same as "m[k1][k2]...[kn]".
func get(data any, keys ...string) any {
for _, key := range keys {
if m, _ := data.(map[string]any); m != nil {
data = m[key]
if data == nil {
break
}
}
}
return data
} And then you can just write: age := get(m, "attributes", "age") which is almost as short as the proposed age := m["attributes"]["age"] and just as easy if not easier to read. A language change is certainly not justified if the same effect can be achieved by writing some code. The bar for a language change is very high. At a minimum it must address a problem that would possibly improve a lot of programs (such as error handling), cannot be done without a language change (generics), or would otherwise significantly improve comfort and/or readability. None of these criteria are met here. This proposal remains a likely decline*. |
I will add that @seankhliao was suggesting essentially the same with a package. If you want specific behavior such as a nil panic, that's obviously trivial to implement as well, with minor changes to the suggested |
Author background
Related proposals
I have not found a related or similar proposal
No
No
Proposal
What is the proposed change?
Allow an index on any / interface{} to be capable to access map[string]any maps on deeper levels more easily. This proposal addresses situations where the map is defined in previous, i.e. where type inference is possible as well as where it is not possible, e.g. unmarshalling json values.
Who does this proposal help, and why?
Anyone who works with undefined data structures in golang. The proposed change helps to access complex data structures more easily.
Please describe as precisely as possible the change to the language.
Allowing to access an any value with an index.
If the key is indexable the value is returned. If the key is not indexable an error is returned that the key is not indexable.
Currently one can do a type assertion on an any type. If the type is not the asserted one it results in a runtime error.
The resulting behavior if the any key is not indexable would stay the same, i.e. a runtime error.
What would change in the language spec?
Allowing to index any
Please also describe the change informally, as in a class teaching Go.
Is this change backward compatible?
Yes. Normal type assertion is not affected
Show example code before and after the change.
It would extend the current capabilities of accessing any values with type assertion with the capability to access these values without a prior type assertion. This would enhance readability and the access to nested data structures more easily.
No
Costs
It would make it easier because it reduces the code and is more intuitive. Many people have at least basic knowledge in scripting languages like JS, TS or Python. The proposed solution would help them to use the benefits of golang with the simplicity that they already know.
2 (Vet and GOPLS)
Probably none, max minimal
None
No
No
The text was updated successfully, but these errors were encountered: