How to access the `<:<` method

Continuing the discussion from Runtime reflection in Scala:

Can someone help me access the <:< method?

val data = List(1, 1.0, "hello", Array("hello",1.0))

val List(c1,c2,c3,c4) = data.map(_.getClass)

c1.<:<( c2)

c1 <:< c2

I get the following error.

Error:(5, 4) value <:< is not a member of Class[T]
c1.<:<( c2)

<:< is part of Scala reflection. getClass and Class are Java reflection.

The equivalent of <:< in Java reflection is isAssignableFrom:

scala 2.13.2> classOf[String].isAssignableFrom(classOf[Object])
val res3: Boolean = false

scala 2.13.2> classOf[Object].isAssignableFrom(classOf[String])
val res4: Boolean = true
4 Likes

So can I import something to get it? Maybe <:< first appears in 2.13?

You can define it

implicit class ClassOps[T](cls: Class[T]) {
  def <:< (cls1: Class[_]): Boolean = cls1 isAssignableFrom cls
}

c1 <:< c2 //false

Or you can use getType instead of getClass

import scala.reflect.runtime.universe._
def getType[A: TypeTag](a: A): Type = typeOf[A]

val List(c1,c2,c3,c4) = data.map(getType)

c1 <:< c2 // true

Notice that data has type List[Any] so both c1 and c2 are Any.

1 Like

It’s old:

scala 2.11.12> :power
Power mode enabled. :phase is at typer.
import scala.tools.nsc._, intp.global._, definitions._
Try :help or completions for vals._ and power._

scala 2.11.12> typeOf[String] <:< typeOf[AnyRef]
res2: Boolean = true

scala 2.11.12> typeOf[AnyRef] <:< typeOf[String]
res3: Boolean = false

If you’re sure Scala reflection is what you want, it’s documented at Overview | Reflection | Scala Documentation

Note that there’s also a class called <:<, which doesn’t involve any imports:

scala 2.11.12> implicitly[String <:< AnyRef]
res0: <:<[String,AnyRef] = <function1>

scala 2.11.12> implicitly[AnyRef <:< String]
<console>:12: error: Cannot prove that AnyRef <:< String.
               implicitly[AnyRef <:< String]
                         ^

You asked specifically about a method, but the class is much more commonly seen, so perhaps the class is what you meant.

What is it that you’re actually trying to do?

3 Likes

Something to underline in Seth’s latter example: AFAIK, <:< is mainly used in type signatures, to prove that a type relationship is correct at compile time. I hadn’t even realized it could be used at runtime…

3 Likes

Or use ClassTag which already has such a method.

2 Likes

Thanks for the link. I think that’ll be useful. Is the reflection API something that is maintained from release to release? The document is marked as EXPERIMENTAL.

1 Like

It will remain experimental for the lifetime of Scala 2.

It’s highly unlikely that the reflection API will undergo any really substantial changes in any future 2.x releases, but smaller changes (including breaking ones) remain possible.

2 Likes

Hi Dmytro, can you elaborate about how this is supposed to work?
Is it the intent that getType returns Any ? and getClass returns the most specific class?

Here is the simple code I’m using in the scala worksheet

import scala.reflect.runtime.universe._
def getType[A: TypeTag](a: A): Type = typeOf[A]

val data = List(1,2.0,"three")

data.map(_.getClass)

data.map(getType)

And here is the output from the worksheet.

import scala.reflect.runtime.universe._
def getType[A](a: A)(implicit evidence$1: reflect.runtime.universe.TypeTag[A]): reflect.runtime.universe.Type

val data: List[Any] = List(1, 2.0, three)

val res0: List[Class[_]] = List(class java.lang.Integer, class java.lang.Double, class java.lang.String)

val res2: List[reflect.runtime.universe.Type] = List(Any, Any, Any)

That output is as-expected, because Type and TypeTag represent compile-time types, but as soon as you List(1,2.0,"three"), you’ve told the compiler to throw away its compile-time knowledge of the types of the individual items and just treat all of them as Anys.

If you look at Dmytro’s original post, he already said so:

Notice that data has type List[Any] so both c1 and c2 are Any

What is it that you’re actually trying to accomplish?

1 Like

I’m trying to understand what dynamic type information is available on data which the compiler thinks is Any. What information can be attained at runtime even if the compiler doesn’t know much information about the types.

1 Like

There are no type information in runtime.

At runtime there are only classes.

hmm… does doesn’t every class correspond to a type? (but not the other way around?)

Can I compute the type given the class at runtime?

If you have a List[Option[Either[String, Int]]] then the most you can recover at runtime is that you have a List[_].

2 Likes

Stephen Compall has an excellent blog post on all this, here: Typelevel | There are more types than classes

Answer 1: The question suggests confused thinking. What are you trying to accomplish?

Answer 2: Yes:

scala 2.13.3> class C; val x: Any = new C
class C
val x: Any = C@6740a11c

scala 2.13.3> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._

scala 2.13.3> val mirror = runtimeMirror(getClass.getClassLoader)
val mirror: reflect.runtime.universe.Mirror = ...

scala 2.13.3> mirror.classSymbol(x.getClass).toType
val res0: reflect.runtime.universe.Type = C

See Symbols, Trees, and Types | Reflection | Scala Documentation

1 Like

Taking a look at the blog post, it is a bit confusing.
In the section * No values, infinite types: method type parameters* the author seems to reach an invalid conclusion from this though experiment. He says assume you can list all the classes in your class path, then reaches a conclusion from which he concludes that G is not a class. That’s a wrong conclusion. Isn’t it? It could also mean that the set of classes is uncountable.

I would guess that the set of types is uncountable, at least it is in the type systems I know more about.

But, in runtime, you can ask for the list of all classes and that would be finite.
However, in compile time, you can not ask for the list of all types; since it is infinite.

Simple example:

  • List[Int]
  • List[List[Int]]
  • List[List[List[Int]]]
  • etc
1 Like

Seth, I’m not sure if you’re really interesting in knowing the details of what I’m trying to accomplish. I’m happy to answer, but I don’t want to bore or burden you excessively.

I’m mainly trying to understand what dynamic capability Scala presents to the programming. When I have a heterogeneous collection of objects, what kind of type/class queries I can do at run-time. I.e., can I search for particular type patterns in such data.

To which extent does the runtime type/class reflection interface differ from that of clojure which is implemented atop the same JVM.

The ultimate goal is to try to describe how much of this type of dynamic type checking can be done in Scala and Clojure atop the Java type system.