Nameof to get names of program elements

C# has a really nice feature called nameof. With this expression you can get the name of a program element in a safe way without repeating it as a string.

Example:

fun process(customer: Customer) {
    checkArgument(customer.isTrustworthy, "customer must be trustworthy")
    ...
}

Here you have to repeat the parameter name customer in the exception message. That is error prone.

It would be nice to have a feature like nameof here:

fun process(customer: Customer) {
    checkArgument(customer.isTrustworthy, "${nameof(customer)} must be trustworthy")
    ...
}

Is something like this on the roadmap?

5 Likes

No, we don’t have anything like that on the roadmap. Given that IntelliJ IDEA’s refactorings do a fairly good job of updating string literals, adding a special language feature just for this case seems to be a bit overkill.

I disagree. String literal search may find many unrelated usages especially for common identifier names, so it couldn’t be trusted really.

12 Likes

Absolutely agree with @ilya.gorbunov. Ruby has long had the :thing syntax which is used wildley different than “thing”.

Semantism is a core pinnacle of strongly-typed languages which Kotlin strongly firmly in. To suggest a string literal solution and leave it up to “IDE refactoring” is literally against the whole belief system which Kotlin is built upon, let alone being a huge known Magic Strings anti-pattern. This is why we have enums and the strongly-typed ‘when’ (Kotlin) / ‘switch’ (Swift) statements, to get AWAY from string-based, non-typed workarounds.

I wouldn’t expect this answer from an intermediate developer, let alone the you guys.

2 Likes

This feature actually exists for properties and function names, but not for local variables (yet). Try this:

class Data(val namedProp: String)
fun namedFunction(): String = ""

fun main(args: Array<String>) {
    println("My property name is '${Data::namedProp.name}'")
    println("My function name is '${::namedFunction.name}'")
}

Compiler actually inlines the corresponding invocations and substitutes the actual names, so there is no runtime reflection in this code.

As for variables: https://youtrack.jetbrains.com/issue/KT-16303

P.S. There is also a minor performance problem related to existing .name support in compiler, but that is not the reason for not using this feature if you needed it, and definitely not a reason to invent any kind of nameof (because .name is the Kotlin way!): https://youtrack.jetbrains.com/issue/KT-16304

9 Likes

Another related use case for variable names:

val `Some Kafka Topic Name` = magicallyDefine<Key, Value>("Some Kafka Topic Name")

Obviously, repeating the name is not super convenient. I could imagine, if magicallyDefine is an inline function, it could potentially access the outer variable name using similar syntax and get the String using the proposed .name semi-reflection:

val `Some Kafka Topic Name` = magicallyDefine<Key, Value>()

Where the name is deducted using syntax like val topic: String = ::return.name, which looks amazingly bad and is not always available.

I guess at this level of complexity it is much better to make a normal named class.

1 Like

@a.kosenkov You can achieve something like that with the delegated properties: http://kotlinlang.org/docs/reference/delegated-properties.html

Another use case is to retrieve names of object properties/functions. Something like nameOf(a::foo).
I have created a feature request: https://youtrack.jetbrains.com/issue/KT-23777

Agree too. one must remember to run a refactoring. With nameof more is automatic. Big value proposition of Kotlin is that there is actually innovation and new features being added. This is one that unquestionably should be added. I use this a lot in C#. it is a great feature.

2 Likes

Out of curiosity, what’s wrong with some first-class functions to handle this?

fun nameOf(callable: KCallable<*>): String = callable.name
fun nameOf(clazz: KClass<*>): String = clazz.simpleName!!
fun nameOf(clazz: Class<*>): String = clazz.simpleName

Use like so


data class MyAwesomeClass(val myAwesomeProperty: Int) {
    fun helloWorld() = Unit
}

fun main(vararg args: String) {
    println(nameOf(MyAwesomeClass::class))
    println(nameOf(MyAwesomeClass::myAwesomeProperty))
    println(nameOf(MyAwesomeClass::helloWorld))
}

Works for classes, properties, functions
the only case I’ve found where it won’t work is local fields.

The idea is not bad, but it won’t work for parameter names. And it would add some (not too big, I think) runtime overhead whereas a nameof operator would be evaluated at compile time.

2 Likes

This would be ideal for use in some annotation parameters. Since annotation parameters with interpolated strings (with compile time constants) are already supported, nameof would be very useful in these circumstances. For example, with AspectJ:

// This predictably fails because MyAnnotation::class.simpleName is not
// a compile-time constant
@Around("@annotation(${MyAnnotation::class.simpleName})")

// This would presumably work, as nameof(MyAnnotation) should resolve
// to a compile-time constant
@Around("@annotation(${nameof(MyAnnotation)})")

nameof would be very useful in combination with kotlin-react:

Instead of writing:

val Login = FC<Props>("Login") {}

val Dashboard = FC<Props>("Dashboard") {}

We could easily link the display name of the component to the variable name:

val Login = FC<Props>(nameof(Login)) {}

val Dashboard = FC<Props>(nameof(Dashboard)) {}

Please be aware that in this case using ::Login.name does not compile.