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."
Related
I mistakenly concatted a string with an Option[String] while coding in scala.
I expected as a strongly typed language, scala would not allow me to do such operation.
This is what I tried.
This works
scala> val a:String = "aaa"
val a: String = aaa
scala> val b:Option[String] = Some("bbbb")
val b: Option[String] = Some(bbbb)
scala> a + b
val res0: String = aaaSome(bbbb)
scala> val c:Option[String] = None
val c: Option[String] = None
scala> val d = a + c
val d: String = aaaNone
scala> val e = 1
val e: Int = 1
scala> a + e
val res2: String = aaa1
while this does not work
scala> val f:Option[String] = Some("ffff")
val f: Option[String] = Some(ffff)
scala> val g:Option[String] = None
val g: Option[String] = None
scala> f + g
^
error: type mismatch;
found : Option[String]
required: String
Why does scala allow such behavior? Dynamically typed languages like python will stop me from adding strings to int types, None types or any type other than strings. Curious if this design is intentional? If so why?
Scala contains an implicit class any2stringadd in it's Predef package. This class is responsible for these concatenation operations.
implicit final class any2stringadd[A](private val self: A) extends AnyVal {
def +(other: String): String = String.valueOf(self) + other
}
What it means is that, by default, scope contains a method + which can concatenate value of any type A with string by converting value of this type to string via String.valueOf(...).
I can't speak of design choices, I agree that this might be an unexpected behavior. The same applies to Scala's native == method. For example, this code compiles just ok: Some("a") == "b". This can lead to nasty bugs in filtering and other methods.
If you want to eliminate this behavior I suggest you take a look at https://typelevel.org/cats/ library, which introduces different typeclasses that can solve this problem.
For example, for string concatenation you can use Semigroup typeclass (which has tons of other useful use-cases as well):
import cats.Semigroup
Semigroup[String].combine("a", "b") // works, returns "ab"
Semigroup[String].combine("a", Some("b")) // won't work, compilation error
This looks tedious, but there is a syntactic sugar:
import cats.implicits._
"a" |+| "b" // works, returns "ab"
"a" |+| Some("b") // won't work, compilation error
// |+| here is the same as Semigroup[String].combine
The same thing applies to == method. Instead you can use Eq typeclass:
import cats.implicits._
"a" == Some("b") // works, no error, but could be unexpected
"a" === Some("b") // compilation error (Cats Eq)
"a" === "b" // works, as expected
"a" =!= "b" // same as != but type safe
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
I'm learning Scala and I want to transform a line of code from Java. I've tried 2 methods in scala, but they didn't work. The class ResultScanner comes from Apache HBase - ResultScanner and the same for the Result class
Java
for(Result r : resultScanner) System.out.println(r)
Scala
while(resultScanner.hasNext) // error[1]
println(resultScanner.next())
//error[1]: value hasNext is not a member of org.apache.hadoop.hbase.client.ResultScanner
I've tried the following as well :
resultScanner.foreach(println(_)) // error[2]
// error[2]: value foreach is not a member of org.apache.hadoop.hbase.client.ResultScanner
Include import scala.collection.JavaConverters._ for Java to Scala collections portability. Then invoke asScala on the collection,
resultScanner.asScala foreach println
Instead of ArrayList create ResultScanner and try:
scala> import java.util
import java.util
scala> import scala.collection.JavaConversions._
import scala.collection.JavaConversions._
scala> val jList = new util.ArrayList[Int]
jList: java.util.ArrayList[Int] = []
scala> jList.add(2)
res4: Boolean = true
scala> jList.add(6)
res5: Boolean = true
scala> val iterable = iterableAsScalaIterable(jList)
iterable: Iterable[Int] = Wrappers(2, 6)
scala> iterable.foreach(println)
2
6
Note:
Choose JavaConverters._ instead of JavaConverters._
scala> import scala.collection.JavaConverters._
import scala.collection.JavaConverters._
scala> import java.util
import java.util
scala> val jList = new util.ArrayList[Int]
jList: java.util.ArrayList[Int] = []
scala> jList.add(2)
res0: Boolean = true
scala> jList.add(6)
res1: Boolean = true
scala> val iterable = jList.asScala
iterable: scala.collection.mutable.Buffer[Int] = Buffer(2, 6)
scala> val iterable:Iterable[Int] = jList.asScala
iterable: Iterable[Int] = Buffer(2, 6)
I have a requirement in which I want to implicitly convert and Iterable to Option. The requirement is if the Iterable is empty I return None otherwise I return Some(iterable).
The code below
implicit def toOption[T](iterable: Iterable[T]): Option[Iterable[T]] = if (iterable.isEmpty) None else Some(iterable)
val opt = List(6,7,8).toOption
I am getting compile error 'value toOption is not a member of
List[Int]'
What am I missing here?
Implicit conversion alone is not going to help you if you want to do this. What you need here is an implicit class :
object Rich {
implicit class RichIterable[T](it: Iterable[T]){
def toOption: Option[Iterable[T]] = if(it.isEmpty) None else Some(it)
}
}
scala> import Rich._
import Rich._
scala> List(1,2,3).toOption
res0: Option[Iterable[Int]] = Some(List(1, 2, 3))
scala> List().toOption
res1: Option[Iterable[Nothing]] = None
You have the calling syntax wrong (toOption is not a method on list this way, it's a top level method/function) and you need to be less specific in your type (so it will work with implicit conversions etc):
implicit def toOption[I <% Iterable[_]](iterable: I): Option[I] = if (iterable.isEmpty) None else Some(iterable)
val opt1: Option[List[Int]] = List(6,7,8)
val opt2: Option[List[Int]] = List()
Here I used a view (<%), meaning as long as the argument can be coerced or transformed to the Iterable type, it's ok.
Also you can also write the assignment like this:
val opt2 = List(): Option[List[Int]]
And here is some documentation on views:
http://docs.scala-lang.org/tutorials/tour/views.html
There's a handy implementation of asInstanceOfOpt, a safe version of asInstanceOf, given in the answer to How to write "asInstanceOfOption" in Scala. It appears that, Scala 2.9.1, this solution now only works with AnyRef:
class WithAsInstanceOfOpt(obj: AnyRef) {
def asInstanceOfOpt[B](implicit m: Manifest[B]): Option[B] =
if (Manifest.singleType(obj) <:< m)
Some(obj.asInstanceOf[B])
else
None
}
Can this be rewritten to support Any?
If you look in the Scala API the function singleType takes a parameter of type AnyRef. I don't really know the background for this decision, but it seems you need to work around it. Instead of using the method singleType I'd suggest using the classType method which basically can make a manifest for any class. It'll take a bit more code, but it could look something like this:
class WithAsInstanceOfOpt(obj : Any) {
def asInstanceOfOpt[B : Manifest] : Option[B] = // [B : Manifest] is shorthand for [B](implicit m : Manifest[B])
if (Manifest.classType(manifest, obj.getClass) <:< manifest)
Some(obj.asInstanceOf[B])
else None
}
You could use shapeless's Typeable from Miles Sabin:
Type casting using type parameter
It handles primitives and boxing:
scala> import shapeless._; import syntax.typeable._
import shapeless._
import syntax.typeable._
scala> 1.cast[Int]
res1: Option[Int] = Some(1)
scala> 1.cast[String]
res2: Option[String] = None
scala> "hello".cast[String]
res4: Option[String] = Some(hello)
scala> "foo".cast[Int]
res5: Option[Int] = None
You can see the source here to see how it's written:
https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/typeable.scala
Here's working code for 2.9.x. It will give deprecation warnings for 2.10.x, but using ClassTag instead of Manifest and runtimeClass instead of erasure will fix them.
//Precondition: classS must have been produced through primitiveToBoxed, because v will be boxed.
def ifInstanceOfBody[T, S](v: T, classS: Class[_]): Option[S] = {
if (v == null || !classS.isInstance(v))
None
else
Some(v.asInstanceOf[S])
}
object ClassUtil {
import java.{lang => jl}
private val primitiveToBoxedMap = Map[Class[_], Class[_]](
classOf[Byte] -> classOf[jl.Byte],
classOf[Short] -> classOf[jl.Short],
classOf[Char] -> classOf[jl.Character],
classOf[Int] -> classOf[jl.Integer],
classOf[Long] -> classOf[jl.Long],
classOf[Float] -> classOf[jl.Float],
classOf[Double] -> classOf[jl.Double],
classOf[Boolean] -> classOf[jl.Boolean],
classOf[Unit] -> classOf[jl.Void]
)
def primitiveToBoxed(classS: Class[_]) =
primitiveToBoxedMap.getOrElse(classS, classS)
}
class IfInstanceOfAble[T](v: T) {
def asInstanceOfOpt[S](implicit cS: Manifest[S]): Option[S] =
ifInstanceOfBody[T, S](v, ClassUtil.primitiveToBoxed(cS.erasure))
}
implicit def pimpInstanceOf[T](t: T) = new IfInstanceOfAble(t)
Testing results:
scala> 1.asInstanceOfOpt[Int]
res9: Option[Int] = Some(1)
scala> "".asInstanceOfOpt[String]
res10: Option[String] = Some()
scala> "foo".asInstanceOfOpt[String]
res11: Option[String] = Some(foo)
scala> 1.asInstanceOfOpt[String]
res12: Option[String] = None
scala> "".asInstanceOfOpt[Int]
res13: Option[Int] = None
The code is slightly more verbose than needed here, mostly because I took it from an existing codebase of mine where I reuse ifInstanceOfBody elsewhere. Inlining into asInstanceOfOpt would fix that and shorten the code somewhat, but most of it is for primitiveToBoxedMap, and trust me that I could not find something like that available in the Scala standard library.