Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the <:< operator work in Scala?

In Scala there's a class <:< that witnesses a type constraint. From Predef.scala:

  sealed abstract class <:<[-From, +To] extends (From => To) with Serializable
  private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }
  implicit def $conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]

An example of how it's used is in the toMap method of TraversableOnce:

def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] =

What I don't understand is how this works. I understand that A <:< B is syntactically equivalent to the type <:<[A, B]. But I don't get how the compiler can find an implicit of that type if and only if A <: B. I assume that the asInstanceOf call in the definition of $conforms is making this possible somehow, but how? Also, is it significant that a singleton instance of an abstract class is used, instead of just using an object?

like image 925
Rag Avatar asked Jun 02 '15 21:06

Rag


People also ask

How do operators work in Scala?

In Scala, all operators are methods. Operators themselves are just syntactic sugar or a shorthand to call methods. Both arithmetic addition (+) and String. charAt() are examples of infix operators.

What does the operator :: mean in Scala?

The ::() operator in Scala is utilized to add an element to the beginning of the List. Method Definition: def ::(x: A): List[A] Return Type: It returns the stated list after adding an element to the beginning of it.

What does === mean in Scala?

The triple equals operator === is normally the Scala type-safe equals operator, analogous to the one in Javascript. Spark overrides this with a method in Column to create a new Column object that compares the Column to the left with the object on the right, returning a boolean.

What is the meaning of _ in Scala?

Scala allows the use of underscores (denoted as '_') to be used as placeholders for one or more parameters. we can consider the underscore to something that needs to be filled in with a value. However, each parameter must appear only one time within the function literal.


1 Answers

Suppose we've got the following simple type hierarchy:

trait Foo
trait Bar extends Foo

We can ask for proof that Bar extends Foo:

val ev = implicitly[Bar <:< Foo]

If we run this in a console with -Xprint:typer, we'll see the following:

private[this] val ev: <:<[Bar,Foo] =
  scala.this.Predef.implicitly[<:<[Bar,Foo]](scala.this.Predef.$conforms[Bar]);

So the compiler has picked $conforms[Bar] as the implicit value we've asked for. Of course this value has type Bar <:< Bar, but because <:< is covariant in its second type parameter, this is a subtype of Bar <:< Foo, so it fits the bill.

(There's some magic involved here in the fact that the Scala compiler knows how to find subtypes of the type it's looking for, but it's a fairly generic mechanism and isn't too surprising in its behavior.)

Now suppose we ask for proof that Bar extends String:

val ev = implicitly[Bar <:< String]

If you turn on -Xlog-implicits, you'll see this:

<console>:9: $conforms is not a valid implicit value for <:<[Bar,String] because:
hasMatchingSymbol reported error: type mismatch;
 found   : <:<[Bar,Bar]
 required: <:<[Bar,String]
       val ev = implicitly[Bar <:< String]
                          ^
<console>:9: error: Cannot prove that Bar <:< String.
       val ev = implicitly[Bar <:< String]
                          ^

The compiler tries the Bar <:< Bar again, but since Bar isn't a String, this isn't a subtype of Bar <:< String, so it's not what we need. But $conforms is the only place the compiler can get <:< instances (unless we've defined our own, which would be dangerous), so it quite properly refuses to compile this nonsense.


To address your second question: the <:<[-From, +To] class is necessary because we need the type parameters for this type class to be useful. The singleton Any <:< Any value could just as well be defined as an object—the decision to use a val and an anonymous class is arguably a little simpler, but it's an implementation detail that you shouldn't ever need to worry about.

like image 83
Travis Brown Avatar answered Oct 04 '22 09:10

Travis Brown