type constraints - How does the <:< operator work in Scala? -
in scala there's class <:<
witnesses type constraint. predef.scala
:
sealed abstract class <:<[-from, +to] extends (from => to) serializable private[this] final val singleton_<:< = new <:<[any,any] { def apply(x: any): = x } implicit def $conforms[a]: <:< = singleton_<:<.asinstanceof[a <:< a]
an example of how it's used in tomap
method of traversableonce
:
def tomap[t, u](implicit ev: <:< (t, u)): immutable.map[t, u] =
what don't understand how works. understand a <:< b
syntactically equivalent type <:<[a, b]
. don't how compiler can find implicit of type if , if a <: b
. assume asinstanceof
call in definition of $conforms
making possible somehow, how? also, significant singleton instance of abstract class used, instead of using object
?
suppose we've got following simple type hierarchy:
trait foo trait bar extends foo
we can ask proof bar
extends foo
:
val ev = implicitly[bar <:< foo]
if run in console -xprint:typer
, we'll see following:
private[this] val ev: <:<[bar,foo] = scala.this.predef.implicitly[<:<[bar,foo]](scala.this.predef.$conforms[bar]);
so compiler has picked $conforms[bar]
implicit value we've asked for. of course value has type bar <:< bar
, because <:<
covariant in second type parameter, subtype of bar <:< foo
, fits bill.
(there's magic involved here in fact scala compiler knows how find subtypes of type it's looking for, it's generic mechanism , isn't surprising in behavior.)
now suppose ask proof bar
extends string
:
val ev = implicitly[bar <:< string]
if turn on -xlog-implicits
, you'll see this:
<console>:9: $conforms not valid implicit value <:<[bar,string] because: hasmatchingsymbol reported error: type mismatch; found : <:<[bar,bar] required: <:<[bar,string] val ev = implicitly[bar <:< string] ^ <console>:9: error: cannot prove bar <:< string. val ev = implicitly[bar <:< string] ^
the compiler tries bar <:< bar
again, since bar
isn't string
, isn't subtype of bar <:< string
, it's not need. $conforms
place compiler can <:<
instances (unless we've defined our own, dangerous), quite refuses compile nonsense.
to address second question: <:<[-from, +to]
class necessary because need type parameters type class useful. singleton any <:< any
value defined object—the decision use val
, anonymous class arguably little simpler, it's implementation detail shouldn't ever need worry about.
Comments
Post a Comment