I'm new to the reflection API.
I'd like to get a reference to an object from its name. I've made it to the point where I can get a reference using the class name of the object.
$ scala
Welcome to Scala version 2.11.7 ...
scala> case object Foo { val x = 5 }
defined object Foo
scala> import scala.reflect.runtime.{universe => ru}
import scala.reflect.runtime.{universe=>ru}
scala> val m = ru.runtimeMirror(getClass.getClassLoader)
m: reflect.runtime.universe.Mirror...
scala> val f = m.reflectModule(m.staticModule(Foo.getClass.getName)).instance.asInstanceOf[Foo.type]
f: Foo.type = Foo
scala> f.x
res0: Int = 5
Works just fine. However, trying to use the computed type name as a string doesn't work:
scala> m.staticModule(Foo.getClass.getName)
res2: reflect.runtime.universe.ModuleSymbol = object iw$Foo$
scala> Foo.getClass.getName
res1: String = Foo$
scala> m.staticModule("Foo$")
scala.ScalaReflectionException: object Foo$ not found.
at scala.reflect.internal.Mirrors$RootsBase.staticModule(Mirrors.scala:162)
at scala.reflect.internal.Mirrors$RootsBase.staticModule(Mirrors.scala:22)
... 33 elided
What am I missing here? Thanks.
This problem appears in REPL only. Try the following in REPL:
scala> Foo.getClass.getName.length
res5: Int = 25
So, the 'Foo$' is not the full name of class Foo
scala> new String(Foo.getClass.getName.getBytes("UTF-8").map(b => if(b==36) '?'.toByte else b), "UTF-8")
res6: String = ?line3.?read??iw??iw?Foo?
And you can call with no problem:
scala>m.staticModule("$line3.$read$$iw$$iw$Foo$")
res7: reflect.runtime.universe.ModuleSymbol = object iw$Foo$
See also: https://issues.scala-lang.org/browse/SI-9335
Related
What does the compiler see differently in each class declaration below that causes typeOf to behave differently. (i.e. what's the property of the class declaration that causes it to fail).
import org.junit.Test
import scala.reflect.runtime.universe._
case class Person1(name: String)
class ReflectTest {
case class Person2(name: String)
#Test
def constructorTest(): Unit = {
case class Person3(name: String)
typeOf[Person1] // Yep
typeOf[Person2] // No problem
typeOf[Person3] // No typetag information available :(
}
}
It's a free type. And you can't refer to it with a prefix.
scala> import reflect.runtime.universe._
import reflect.runtime.universe._
scala> class C { class D }
defined class C
scala> val c = new C
c: C = C#29626d54
scala> val x = { final class X ; weakTypeTag[X] }
x: reflect.runtime.universe.WeakTypeTag[_ <: AnyRef] = WeakTypeTag[X]
scala> val t = typeTag[C]
t: reflect.runtime.universe.TypeTag[C] = TypeTag[C]
scala> val w = typeTag[c.D]
w: reflect.runtime.universe.TypeTag[c.D] = TypeTag[c.D]
scala> val v = typeTag[C#D]
v: reflect.runtime.universe.TypeTag[C#D] = TypeTag[C#D]
scala> (t.tpe.typeSymbol, t.tpe.typeSymbol.isStatic)
res1: (reflect.runtime.universe.Symbol, Boolean) = (class C,true)
scala> (x.tpe.typeSymbol, x.tpe.typeSymbol.isStatic)
res2: (reflect.runtime.universe.Symbol, Boolean) = (free type X,false)
scala> (w.tpe.typeSymbol, w.tpe.typeSymbol.isStatic)
res3: (reflect.runtime.universe.Symbol, Boolean) = (class D,false)
scala> (v.tpe.typeSymbol, v.tpe.typeSymbol.isStatic)
res4: (reflect.runtime.universe.Symbol, Boolean) = (class D,false)
scala> reflect.runtime.universe.internal.asFreeType(x.tpe.typeSymbol)
res5: reflect.runtime.universe.FreeTypeSymbol = free type X
Other things you can't do with local classes, as mentioned in the spec:
scala> val x = { private class X ; weakTypeTag[X] }
<console>:1: error: illegal start of statement (no modifiers allowed here)
val x = { private class X ; weakTypeTag[X] }
^
scala> import c.D
import c.D
I'd look more but it's late and my REPL broke...
scala> val y = { final class X { class Y } ; val x = new X ; import x.Y ; weakTypeTag[Y] }
warning: there was one feature warning; re-run with -feature for details
java.lang.AssertionError: assertion failed: x.type
at scala.reflect.internal.tpe.TypeMaps$adaptToNewRunMap$.adaptToNewRun(TypeMaps.scala:1106)
at scala.reflect.internal.tpe.TypeMaps$adaptToNewRunMap$.apply(TypeMaps.scala:1150)
at scala.reflect.internal.tpe.TypeMaps$adaptToNewRunMap$.apply(TypeMaps.scala:1079)
at scala.reflect.internal.tpe.TypeMaps$adaptToNewRunMap$.apply(TypeMaps.scala:1148)
at scala.reflect.internal.tpe.TypeMaps$TypeMap.mapOver(TypeMaps.scala:162)
at scala.reflect.internal.tpe.TypeMaps$adaptToNewRunMap$.apply(TypeMaps.scala:1197)
at scala.reflect.internal.tpe.TypeMaps$adaptToNewRunMap$.apply(TypeMaps.scala:1171)
at scala.reflect.internal.Symbols$Symbol.adaptInfos(Symbols.scala:1629)
at scala.reflect.internal.Symbols$Symbol.rawInfo(Symbols.scala:1581)
at scala.tools.nsc.typechecker.Typers$Typer.isStale(Typers.scala:504)
at scala.tools.nsc.typechecker.Typers$Typer.reallyExists(Typers.scala:496)
at scala.tools.nsc.typechecker.Typers$Typer.typedSelectInternal$1(Typers.scala:4712)
at scala.tools.nsc.typechecker.Typers$Typer.typedSelect$1(Typers.scala:4676)
In the REPL there's a command to print the type:
scala> val s = "House"
scala> import scala.reflect.runtime.universe._
scala> val li = typeOf[List[Int]]
scala> :type s
String
scala> :type li
reflect.runtime.universe.Type
How can I get this ":type expr" functionality in my Scala program to print types?
Let me clarify the ":type expr" functionality I would like to have, something like this:
println(s.colonType) // String
println(li.colonType) // reflect.runtime.universe.Type
How can I get such a "colonType" method in my Scala program outside the REPL (where I don't have the :type command available)?
I looked into the sources of the REPL (or better of scalac which contains the REPL) and found this (Source):
private def replInfo(sym: Symbol) = {
sym.info match {
case NullaryMethodType(restpe) if sym.isAccessor => restpe
case info => info
}
}
Method info of scala.reflect.internal.Symbols.Symbol returns a Type (Source). Later toString is called on this type, therefore you should do the same if you want the same type information:
scala> import scala.reflect.runtime.{universe => u}
import scala.reflect.runtime.{universe=>u}
scala> u.typeOf[List[String]].toString
res26: String = scala.List[String]
scala> u.typeOf[Int => String].toString
res27: String = Int => String
The following implicit conversion should do the trick for you:
import reflect.runtime.universe._
implicit class ColonTypeExtender [T : TypeTag] (x : T) {
def colonType = typeOf[T].toString
}
Is that what you'd like to achieve?
val s = "House"
println(s.getClass.getName) // java.lang.String
println(s.getClass.getSimpleName) // String
Tested with Scala REPL 2.11 :
Add _ after function name to treat it as partially applied function.
Example :
scala> def addWithSyntaxSugar(x: Int) = (y:Int) => x + y
addWithSyntaxSugar: (x: Int)Int => Int
scala> addWithSyntaxSugar _
res0: Int => (Int => Int) = <function1>
scala>
When I use :load in the spark-shell it appears as though lines are read separately and thus companion objects are not read in the same "source" file. :paste does not appear to take arguments.
Previously I was building and loading a jar with my code into the spark-shell but was hoping to run it as a script for simplicity. Does anyone have a favorite workaround?
A sufficiently recent shell will have :paste file.
Or, as a workaround, link the templates this way to :load them:
class C(i: Int) {
def c = { println("C..."); i }
}; object C {
def apply(i: Int = 42) = new C(i)
}
Or,
scala> (new $intp.global.Run) compile List("C.scala")
scala> new C().c
C...
res1: Int = 42
More API:
scala> import reflect.io._
import reflect.io._
scala> import reflect.internal.util._
import reflect.internal.util._
scala> val code = File("C.scala").slurp
code: String =
"
class C(i: Int) { def c = { println("C..."); i } }
object C { def apply(i: Int = 42) = new C(i) }
"
scala> $intp interpret code
defined class C
defined object C
res0: scala.tools.nsc.interpreter.IR.Result = Success
scala> C()
res1: C = C#f2f2cc1
Similarly,
scala> $intp interpret s"object X { $code }"
defined object X
res0: scala.tools.nsc.interpreter.IR.Result = Success
scala> X.C()
res1: X.C = X$C#7d322cad
My startup script defines:
implicit class `interpreter interpolator`(val sc: StringContext) { def i(args: Any*) = $intp interpret sc.s(args: _*) }
for
scala> i"val x = 42"
x: Int = 42
res0: scala.tools.nsc.interpreter.IR.Result = Success
This compile trick doesn't appear to work with "script" files. It expects a source file compilable by scalac, where all val and def declarations are inside a type.
So, an alternative hack that will work with :load is to write the case class and companion object inside another object. Here I just pasted the code, without using :paste, but it works with :load, too.
scala> object O {
| case class C(s: String)
| object C {
| def apply() = new C("<no string>")
| }
| }
defined module O
scala> O.C()
res0: O.C = C(<no string>)
I have a testing program in which we have in memory static arrays. I am using type aliases for brevity.
The following works in the REPL
type >[T] = Array[T]
val dat = >(>(1,2,3),>(2,3,4))
dat: Array[Array[Int]] = Array(Array(1, 2, 3), Array(2, 3, 4))
However changing the identifier to "A" from ">" does not work: the type is created but the same syntax for creating the array as used above fails:
scala> type A[T] = Array[T]
defined type alias A
scala> val dat = A(A(1,2,3),A(2,3,4))
<console>:7: error: not found: value A
val dat = A(A(1,2,3),A(2,3,4))
Also, NEITHER of the two above work within a Scala program AFAICT:
test("VectorProjection") {
type A[T] = Array[T]
// Next line shows RED for all the A's and also has compiler error: "not found: value A"
val dat = A(A(1., 2., 3.), A(1.5,2.,2.5), A(2.,3.8,5.6), A(2.5,3.0,3.5), A(3.1,3.7,4.3) )
val firsteigen = subtractProject(dat(0), dat(4))
}
Looking for:
1) For the REPL: An explanation of why the symbol ">" works but not
the identifier would be helpful.
2) For a real scala program/class:
An explanation of if it were possible to use any syntax similar to
the above
UPDATE Per suggestion by James Iry the following approach does work :
def A[T : ClassTag](ts: T*) = Array(ts:_*)
Here it is in action:
test("VectorProjection") {
def A[T : ClassTag](ts: T*) = Array(ts:_*)
val dat = A(
A(1., 2., 3.),
A(1.5,2.,2.5),
A(3.,6.,9.) )
val firstEigen = subtractProject(dat(0), dat(5))
println(s"firstEigen: ${firstEigen.mkString(",")}")
}
Another UPDATE Another answer hits closer to this OP:
Use type and val together:
type A = Array[Double]
val A = Array
Here it is in action:
test("VectorProjection") {
type A = Array[Double]
val A = Array
val dat = A(
A(1., 2., 3.),
A(1.5,2.,2.5),
A(3.,6.,9.) )
val firstEigen = subtractProject(dat(0), dat(5))
println(s"firstEigen: ${firstEigen.mkString(",")}")
}
I'm not able to replicate your success with '>'
scala> type >[T]=Array[T]
defined type alias $greater
scala> >(1,2,3)
<console>:8: error: not found: value >
>(1,2,3)
^
At least, not until I define it
scala> import scala.reflect._
import scala.reflect._
scala> def >[T : ClassTag](ts: T*) = Array(ts:_*)
$greater: [T](ts: T*)(implicit evidence$1: scala.reflect.ClassTag[T])Array[T]
scala> >(1,2,3)
res1: Array[Int] = Array(1, 2, 3)
Same thing works for A
scala> type A[T]=Array[T]
defined type alias A
scala> A(1,2,3)
<console>:11: error: not found: value A
A(1,2,3)
^
scala> def A[T : ClassTag](ts: T*) = Array(ts:_*)
A: [T](ts: T*)(implicit evidence$1: scala.reflect.ClassTag[T])Array[T]
scala> A(1,2,3)
res2: Array[Int] = Array(1, 2, 3)
For an explanation: type X = Y just creates a synonym for the type X. It doesn't bring in synonyms for everything else that might be associated with the type like companion objects, constructor methods, etc.
If you create a value alias it will work:
type A[T] = Array[T]
val A = Array
val dat = A(A(1,2,3),A(2,3,4)) //dat: Array[Array[Int]] = Array(Array(1, 2, 3), Array(2, 3, 4))
Line 2 creates a value alias, so you can create values with A type alias. It will in turn be able to call A.apply(1,2,3).
Use this to show what the repl knows:
scala> $intp.definedTerms
res0: List[$intp.global.TermName] = List($intp)
scala> $intp.definedTypes
res1: List[$intp.global.TypeName] = List($greater)
E.g., you might have:
scala> object X
defined object X
scala> trait X
defined trait X
warning: previously defined object X is not a companion to trait X.
Companions must be defined together; you may wish to use :paste mode for this.
scala> type X = String
defined type alias X
But it doesn't warn on aliasing.
I'm having trouble understanding why using scala's runtime reflection in 2.11.1 gives me seemingly inconsistent results.
I am trying to inspect the type of a field contained in a java object, like this one:
import java.util.List;
import java.util.ArrayList;
public class Example {
private List<Integer> listOfInts;
public Example () {
listOfInts = new ArrayList<Integer>();
}
}
Now suppose I have a scala program that tries to reason about the type of the field inside "Example:"
import java.lang.Class
import java.lang.reflect.Field
import java.util.List
import scala.reflect.runtime.{ universe => ru }
object Inspect extends scala.App {
val example = new Example
val cls = example.getClass
val listfield = cls.getDeclaredField("listOfInts")
println(isListType(listfield)) // prints false
println(isListType(listfield)) // prints true, as do all subsequent calls
def isListType (field: Field): Boolean = {
/*
A function that returns whether the type of the field is a list.
Based on examples at http://docs.scala-lang.org/overviews/reflection/environment-universes-mirrors.html
*/
val fieldcls = field.getType
val mirror: ru.Mirror = ru.runtimeMirror(getClass.getClassLoader)
val fieldsym: ru.ClassSymbol = mirror.classSymbol(fieldcls)
val fieldtype: ru.Type = fieldsym.toType
(fieldtype <:< ru.typeOf[List[_]])
}
}
In this particular code snippet, the first call to isListType returns false, and the second returns true. If I switch the type operator from <:< to =:=, the first call returns true, and the second false.
I have a similar function in a larger code body, and have found that even when the function is part of a static object, this behavior occurs. This does not happen when using unparameterized classes. While I intended for the function to be pure, this is obviously not the case. Further experimentation has shown that there is some persistent state held somewhere. If I replace the isListType function with straightline code, I get this:
...
val example = new Example
val cls = example.getClass
val listfield = cls.getDeclaredField("listOfInts")
val fieldcls = listfield.getType
val mirror: ru.Mirror = ru.runtimeMirror(getClass.getClassLoader)
val fieldsym: ru.ClassSymbol = mirror.classSymbol(fieldcls)
val fieldtype: ru.Type = fieldsym.toType
println(fieldtype <:< ru.typeOf[List[_]]) // prints false
println(fieldtype <:< ru.typeOf[List[_]]) // prints false
but if I reassign to field type after the <:< operator, I get this:
// replace as under the fieldsym assignment
var fieldtype: ru.Type = fieldsym.toType
println(fieldtype <:< ru.typeOf[List[_]]) // prints false
fieldtype = fieldsym.toType
println(fieldtype <:< ru.typeOf[List[_]]) // prints true
while reassigning to field type before the <:< operator gives this:
// replace as under the fieldsym assignment
var fieldtype: ru.Type = fieldsym.toType
fieldtype = fieldsym.toType
println(fieldtype <:< ru.typeOf[List[_]]) // prints false
println(fieldtype <:< ru.typeOf[List[_]]) // prints false
Does anyone understand what I'm doing wrong here, or at least have a way around this?
The reflection library is based on the compiler, which is a crying shame. People should demand better. Anyway, this is just how it is.
Here's a sample ticket from almost two years ago. https://issues.scala-lang.org/browse/SI-6826
there is some persistent state held somewhere
There's almost nothing but.
Addendum: for a truly dizzying experience, page through a selection of the 355 open reflection tickets.
I don't know about mixing Java reflection and Scala reflection, but symbols do need initialization, as I remember past issues around lack thereof. Surely the re-assignment is not relevant, but maybe after the first <:< the state of the Symbol changes.
usage:
scala> import reflect.runtime._ ; import universe._
import reflect.runtime._
import universe._
scala> typeOf[jex.Example]
res0: reflect.runtime.universe.Type = jex.Example
scala> .declarations
warning: there was one deprecation warning; re-run with -deprecation for details
res1: reflect.runtime.universe.MemberScope = SynchronizedOps(variable listOfInts, constructor Example)
scala> typeOf[jex.Example] member (TermName("listOfInts"))
res2: reflect.runtime.universe.Symbol = variable listOfInts
scala> .typeSignature
res3: reflect.runtime.universe.Type = java.util.List[Integer]
Sorry if I'm too distracted to read your question correctly.
Here's my attempt to reproduce:
scala> import reflect.runtime._ ; import universe._
import reflect.runtime._
import universe._
scala> classOf[jex.Example].getDeclaredField("listOfInts").getType
res0: Class[_] = interface java.util.List
scala> currentMirror classSymbol res0 toType
warning: there was one feature warning; re-run with -feature for details
res1: reflect.runtime.universe.Type = java.util.List
scala> .<:<(typeOf[List[_]])
res2: Boolean = false
scala> currentMirror classSymbol res0 toType
warning: there was one feature warning; re-run with -feature for details
res3: reflect.runtime.universe.Type = java.util.List[E]
scala> .<:<(typeOf[List[_]])
res4: Boolean = false
Another attempt:
scala> import reflect.runtime._ ; import universe._
import reflect.runtime._
import universe._
scala> val x = new jex.Example
x: jex.Example = jex.Example#1efed156
scala> x.getClass getDeclaredField "listOfInts" getType
warning: there was one feature warning; re-run with -feature for details
res0: Class[_] = interface java.util.List
scala> val m = runtimeMirror(getClass.getClassLoader)
m: reflect.runtime.universe.Mirror = JavaMirror with scala.tools.nsc.interpreter.IMain$TranslatingClassLoader#1ffd0e4b of type class scala.tools.nsc.interpreter.IMain$TranslatingClassLoader with classpath [(memory)] and parent being scala.reflect.internal.util.ScalaClassLoader$URLClassLoader#3b084709 of type class scala.reflect.internal.util.ScalaClassLoader$URLClassLoader with classpath [file:/home/apm/jdk-8/jdk1.8.0_11/jre/lib/resources.jar,file:/home/apm/jdk-8/jdk1.8.0_11/jre/lib/rt.jar,file:/home/apm/jdk-8/jdk1.8.0_11/jre/lib/jsse.jar,file:/home/apm/jdk-8/jdk1.8.0_11/jre/lib/jce.jar,file:/home/apm/jdk-8/jdk1.8.0_11/jre/lib/charsets.jar,file:/home/apm/jdk-8/jdk1.8.0_11/jre/lib/jfr.jar,file:/home/apm/scala-2.11.2/lib/akka-actor_2.11-2.3.4.jar,file:/home/apm/scala-2.11.2/lib/config-1.2...
scala> val s = m classSymbol res0
s: reflect.runtime.universe.ClassSymbol = trait List
scala> var t = s.toType
t: reflect.runtime.universe.Type = java.util.List[E]
scala> t <:< typeOf[List[_]]
res1: Boolean = false
scala> t = s.toType
t: reflect.runtime.universe.Type = java.util.List[E]
scala> t <:< typeOf[List[_]]
res2: Boolean = false
OK, so I verified your test class. If the REPL behaves differently, it's probably due to side effects because of printing results.
So adding this to your test
val fieldsym: ru.ClassSymbol = mirror.classSymbol(fieldcls)
println(fieldsym)
val fieldtype: ru.Type = fieldsym.toType
println(fieldtype)
fixes the issue.
apm#mara:~/tmp$ vi jex/inspect.scala
apm#mara:~/tmp$ scalac jex/inspect.scala && scala jex.Inspect
false
true
apm#mara:~/tmp$ vi jex/inspect.scala
apm#mara:~/tmp$ scalac jex/inspect.scala && scala jex.Inspect
trait List
java.util.List[E]
true
trait List
java.util.List[E]
true
I don't know if there's a lesson about "initialize your Syms!"
"At Syms, an educated consumer is our best customer."