开发者

Get Scala variable name at runtime

开发者 https://www.devze.com 2023-02-11 21:26 出处:网络
Is it possible to get the name of a scala variable at runtime? E.g. is it possible to write a function getIntVarName(variable: Int): String behaving as follows?

Is it possible to get the name of a scala variable at runtime?

E.g. is it possible to write a function getIntVarName(variable: Int): String behaving as follows?

val myInt = 3
assert("myInt" === getIn开发者_运维问答tVarName(myInt))


For what you need to do, It seems to me that runtime is not required, since you already have your myInt variable defined at compile time. If this is the case, you just need a bit of AST manipulation via a macro.

Try

package com.natalinobusa.macros

import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context

object Macros {

  // write macros here
  def getName(x: Any): String = macro impl

  def impl(c: Context)(x: c.Tree): c.Tree = {
    import c.universe._
    val p = x match {
      case Select(_, TermName(s)) => s
      case _ => ""
    }
    q"$p"
  }
}

Be aware that macro's must be compiled as a separate subproject, and cannot be part of the same project where the macro substitution has to be applied. Check this template on how to define such a macro sub-project: https://github.com/echojc/scala-macro-template

scala> import Macros._
import Macros._

scala> val myInt = 3
myInt: Int = 3

scala> "myInt" == getName(myInt)
res6: Boolean = true


You can use scala-nameof to get a variable name, function name, class member name, or type name. It happens at compile-time so there's no reflection involved and no runtime dependency needed.

val myInt = 3
assert("myInt" === nameOf(myInt))

will compile to:

val myInt = 3
assert("myInt" === "myInt")


Basically, it can't be done.

The JVM offers nothing by way of a Method handle (remember, Scala properties are encoded as methods in bytecode to support the uniform access principle). The closest you can get is to use reflection to find a list of methods defined on a particular class - which I appreciate doesn't help with your particular need.

It is possible to implement this as a Scala feature, but it would require a compiler plugin to grab the relevant symbol name from the AST and push it into code as a string literal, so not something I could demonstrate in a short code snippet :)

The other naming problem that often comes up in reflection is method parameters. That one at least I can help with. I have a work-in-progress reflection library here that's based on the compiler-generated scala signature as used by scalap. It's nowhere near being ready for serious use, but it is under active development.


Scala doesn't yet have much more than Java in terms of metadata like this. Keep an eye on the Scala Reflection project, but I doubt that will offer access to local variables anytime soon. In the meantime, consider a bytecode inspector library like ASM. Another big caveat: local variable names are lost during compilation, so you'd need to compile in "debug" mode to preserve them.


I don't think it's possible to get the name of a variable, but you can try it with objects:

object Test1 {
  def main(args: Array[String]) {
    object MyVar {
      def value = 1
    }
    println(MyVar.getClass)
  }
}

This prints: class Test1$MyVar$2$. So you can get 'MyVar' out of it.


This can be achieved with Scala 3 Macros (does it at compile time).

Create a Macro object (this must be in a separate file):

import scala.quoted.{Expr, Quotes}

object NameFromVariable :

  def inspectCode(x: Expr[Any])(using Quotes): Expr[String] =
    val name = x.show.split("""\.""").last
    Expr(name)

Then you need an inline method in your class.

inline def getIntVarName(inline x: Any): Any = ${ NameFromVariable.inspectCode('x) }

And use this method, like:

val myInt = 3
assert("myInt" === getIntVarName(myInt))

See the official documentation: https://docs.scala-lang.org/scala3/guides/macros/macros.html

0

精彩评论

暂无评论...
验证码 换一张
取 消