-
Notifications
You must be signed in to change notification settings - Fork 18k
Proposal: Go 2: add C# like nameof() built-in function #37039
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
Comments
What are the rules for that? I would expect it to output AstExpression. At least, C# works that way. |
@creker: You are right. I fixed this in the proposal. |
We started down this path with a suggestion for a way to get the names of the fields of a struct type as a |
@ianlancetaylor, indeed, as we discussed in the other issue, that use case is not necessary and makes nameof() confusingly different from what is in C#. By staying close to the C# nameof we can stick to something "tried and tested", that will be familiar to some people. However, as you can see from the examples above, nameof() could still be used to get the names of struct fields one by one, with a compile time error if the field does not exist. That is also useful for ORM, etc. |
Also, to show the utility of a nameof() construct in a programming language, consider that in the C/C++/Objective-C langauges we have the C preprocessor with the |
Can't this be already done via reflect? |
@qtplatypus, No, the C# like nameof() in this proposal produces compile time constants, based on the names of the variables, which is not possible in the general case with reflect. Only the struct field case is covered, but this nameof proposal is much wider, as it allows to get names of anything that has a name and is in scope. Furthermore with reflect, you would get a you won't get a compile time error in case the field you are looking up does not exist. |
@beoran I'm not even sure it's possible to get a name of the field with reflect. I'm not a reflect expert but poking around I didn't find any obvious way that would do this
|
Indeed, if it is a non exported field, reflect does not work. In this case, nameof() would work fine. |
@beoran it doesn't even matter whether it's exported or not. I just don't see a way how can I go from struct field to its name at runtime. Only thing I can get from it with reflect is Type and Value. Both do not provide any link to the parent struct nor contain any information that it's actually a struct field. |
We can see the utility of this, it would be simple to implement, and it is backward compatible. But it's not quite clear how useful this would be. It would help to point out some real existing code that would benefit from this. Are there places where the standard library would use this functionality? In the logrus example above, the "after" code seems worse than the "before" code. In general |
The logrus example seems worse after, but it is in fact better because it makes it easier to refactor the variable name with tools, and to keep the string and the name of the variable matched if it changes. Speaking for myself, the code in https://gitlab.com/beoran/muesli, an embeddable scripting language interpreter written in Go, for example in https://gitlab.com/beoran/muesli/-/blob/master/ast.go would be helped by this. For example: As for the Go standard library and runtime. I had a quick look and I found a few points where I think nameof could be useful:
I would like other people who gave thumbs up to this issue to contribute examples of how they would like to use |
While it may be true that using Keeping the name of a function and a string description in a test seems fairly minor. We can't really use Still looking for clearer examples of where this is really useful. It's also worth noting that the ease of refactoring due to |
I understand you would like more examples but l have more or less reached the point where I can't think of more ways of how I'd like to use nameof(), without having it to play around with. So as I said before, I would like others interested in this feature also provide examples. I don't feel confident in my ability to implement a prototype for this feature myself, otherwise I would play around a bit to see how I would use nameof. |
I also don't have anything significant to add. Provided examples already cover pretty much everything I expect nameof to be useful for. In C# it was also solving significant pain point around implementing INotifyPropertyChanged interface. That doesn't apply to Go so we're left with everything else - logging, error augmentation, query building. All in the name of allowing safe refactoring. |
On the point of refactoring, I do think that code that is easier to refactor but slightly harder to read is better than code that looks easier to read but is harder to refactor. In general, I feel many Go programmers value ease of refactoring greatly. This is why many people program Go from an IDE, which facilitates refactoring. The nameof built in function would be a feature that helps refactoring in IDE more easily in some cases. |
In the absence of compelling examples, this is a likely decline. Leaving open for four weeks for final comments. |
Well, the examples we gave are perhaps enough to show that nameof() would be convenient, but I guess that to the Go developers, they do not demonstrate enough of an improvement to the Go language to warrant implementation. I would like to ask the others who gave this issue thumbs up, or anyone else who reads this and likes the idea, again to provide some more examples as well. We will need them to convince the Go language developers that nameof() is worth while. I have proposed this issue for the benefit of the Go community, but if there is not enough interest, the Go team is well justified in pursuing more pressing issues. |
No change in consensus. Closing. |
@beoran I know I'm mad late on this issue, and I'm not an experienced Go developer by any means (exp'd outside of Go), but if you decide to propose this in the future: testing. Currently using testify to do some simple interface mocks, and there's a need to bind to method signatures on a type: I'll also just throw my 2c in for doing anything to get rid of magic-strings and sentinel value paradigms in any statically typed language-- they are faux-simpler, horrible to use at scale, and demonstrate a very weak static type system. I feel For API concerns: it would probably have to function with package-level visibility, which is in line with Go in general. Also, thank you to those who proposed this for making the effort. Sorry I came so late-- just hit the frustration point enough to even Google this. :) |
Well, the issue was already closed, so I'm sorry but I think it's too late now, at least for the time being. But thanks for your examples, they really show the value of a built-in nameof. If more people come in to support this idea, I might consider resubmitting this issue with improvements. |
@beoran I'm late too, but I'd like to show you my use case. I have a dbColumnName := db.NamingStrategy.ColumnName(db.NamingStrategy.TableName(nameof(MyStruct)), nameof(MyStruct.MyField)) Just like that, my code is now safe from typing errors in the column names and we might change the naming strategy without needing to change many places in code. |
Heylow, too late as well, but frankly I could not have been sooner since I am learning Go since this week only ;-) Being so new, I cannot give Go examples, but coming from C# I can give a little example on how it's been useful to me: Like @beoran mentionned above, I too value safe refactoring much more than ease of reading. Also, in an IDE, being able to "Find all references" of something is so much useful. |
For the latecomers, Inlike many projects, the Go project does not use GitHub Issues for general discussion or asking questions. Please direct your discussion to other forums. Thank you.
|
Proposal
I propose to add C# like nameof built-in compile time function. This proposal is based on the C# standard here: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/nameof. The main use of this is to make error values and logging easier to refactor, especially when using tools such as go pls, and also for use in ORM and intepreters where the name of an identifier, constant or stuct member, might often have to be mapped to an identical string value.
nameof built-in function
The nameof built-in function obtains the name of a variable, type, or member
as a string constant:
As the preceding example shows, in the case of a type and a package, the produced name is not fully qualified. The same goes for struct members, only the name of the last field in the chain of field accesses is produced.
The nameof built-in function is evaluated at compile time and has no effect at run time.
You can use the nameof built-in function or ORM, for constants that evaluate to themselves as a string value, and for debugging, or to make error values or log messages more maintainable. For example:
Proposal template
I am experienced, I have been using Go for 10 years since before v1.0.
Batch files, Basic, C, C++, D, Java, Javascript, Lua, Objective-C, Oberon, Pascal, PHP, Python, R, Ruby, Shell scripts, Visual Basic, ... I do not have experience with C#, but in the previous issues, it seemed that a C#-like nameof was the one most people seemed interested in for Go as well.
It would not make Go easier to learn, but also not substantially harder. If the new built-in function is well documented, everyone who doesn't know it yet can read its documentation to find out how it works. Furthermore, it will be familiar to C# users.
This idea is based on the proposal proposal: Go 2: obtain struct field name in type-safe way #36332 and proposal: Go 2: add nameof() built-in function #36924.
In stead of special syntax I proposed a built-in function, which is more Go-like and easier to learn. Furthermore, it is not based on reflection, but a compile time,, similar as it is in C# which has proven utility in that language, and it seems like most people are interested in this kind of nameof() function.
This proposal would help people writing ORM's, interpreters, or with debugging aid, and other people who need to use the names of Go language artifacts, such as struct field names, or type names have to be used as strings. It is especially convenient to prevent errors during refactoring when using tools such as go pls.
Yes, adding a new built in function is normally completely backwards compatible, and does not break the Go1 compatibility guarantee.
Example: Logging and error values:
Before:
After:
While the before looks simpler, it has a problem: when you rename the
name
variable, especially using tools, "name" in the string will not be changed by the tool as well. The nameof function can be supported by such tools, and then refactor the name correctly so the error ans log message will also be correct.Example: Orm-ish use:
Before:
After:
In this case, the after code is also somewhat more complex, but it is definitely more correct. If the struct layout changes, then in stead of getting a run time error, we will get a compile time error which is far preferable in such a situation.
The function has to be implemented, documented and tested.
The gopls tool may or may not have to be updated to allow refactoring of the contents of a nameof() call.
Low, because only if the function is used, the compiler has at do a few name and field lookups, but it should already have that data available.
None, the whole point is to have this functionality at compile time.
See above for the proposal based on C# documentation, extended with some more situations.
The Built-in_functions section would need to get a sub chapter on "Obtaining Names" or such.
It does not overlap with reflect, except maybe for names of fields, but nameof() can also fetch names of non-expressions, which is not possible using reflect. It would also allow to check on compile time whether a variable or constant exists: eg:
No.
Only in that it improves the quality of error values in when renaming variables.
No.
The text was updated successfully, but these errors were encountered: