Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get the actual object referred to by Scala 2.10 reflection?

Please consider this code:

object ResponseType extends Enumeration {
  val Listing, Album = Value
}

I can get the Symbol referring to this object like so:

import reflect.runtime.universe._
val s = typeOf[ResponseType.Value].asInstanceOf[TypeRef].pre.typeSymbol

Now, having this symbol, how can I get the actual ResponseType object?

like image 870
Nikita Volkov Avatar asked Aug 26 '12 08:08

Nikita Volkov


People also ask

How to use reflection in Scala?

Types obtained through reflection can be instantiated by invoking their constructor using an appropriate “invoker” mirror (mirrors are expanded upon below). Let's walk through an example using the REPL: scala> case class Person(name: String) defined class Person scala> val m = ru. runtimeMirror(getClass.

What is manifest in Scala?

A Manifest[T] is an opaque descriptor for type T. Its supported use is to give access to the erasure of the type as a Class instance, as is necessary for the creation of native Arrays if the class is not known at compile time.

What is the use of symbol in Scala?

Symbols are used to establish bindings between a name and the entity it refers to, such as a class or a method. Anything you define and can give a name to in Scala has an associated symbol.


1 Answers

scala> val moduleClass = typeOf[ResponseType.Value].asInstanceOf[TypeRef].pre.typeSymbol
moduleClass: reflect.runtime.universe.Symbol = object ResponseType

scala> val module = moduleClass.owner.typeSignature.member(moduleClass.name.toTermName)
module: reflect.runtime.universe.Symbol = object ResponseType

scala> reflect.runtime.currentMirror.reflectModule(module.asModule).instance
res9: Any = ResponseType

Now, some explanation is in order, since this is quite an obscure implementation detail we've (yet!) been unable to abstract over in the public API.

For every object Scala creates an underlying class that represents its signature, internally called module class. For example, if you compile object C the compiler will generate C$.class. That's exactly the module class.

Note that module classes are different from companion classes. Say, for case class C, the compiler will generate three symbols: type C, term C and (another one) type C, where the first type C represents the class C (which contains auto-generated copy, productPrefix, productArity etc) and the second type C represents a signature of object C (which contains auto-generated factory, extractor etc). There won't be any name clashes, because the module class isn't added to the symbol table directly and is only available through <module>.moduleClass.


So what you actually get from your typeOf[ResponseType.Value].asInstanceOf[TypeRef].pre.typeSymbol incantation is a symbol that stands for a module class. There's no function in the API that would get you to a module symbol from a module class. Internally in the compiler there surely is one, but we decided not to expose this implementation detail, because it might very well change soon.

To get to a source module you need to go to owner, peek into the list of its members and look up an object there having the same name as the module class has. That's exactly what moduleClass.owner.typeSignature.member(moduleClass.name.toTermName) does. One minor caveat is that if in the same scope you have a method with the same name, then member will return an overloaded symbol, and you'll need to do something like .member(...).suchThat(_.isModule).

After that it's a piece of cake.


Edit. Actually we're thinking of introducing ClassSymbol.module that would return source module symbol for a module class and NoSymbol otherwise. Quite probably this will end up in RC1. Follow the release notes.

like image 144
Eugene Burmako Avatar answered Sep 20 '22 03:09

Eugene Burmako