This post briefly introduces a useful technique for comparing types (shown to me by Jason Zaugg) that will be used to check the results of type-level computations later.
It uses the ‘implicitly’ method, which is defined in Predef as:
def implicitly[T](implicit e: T): T = e
This is useful for capturing an implicit value that is in scope and has type T. The implicit that we want in this case is A =:= B for some types A and B. A =:= B will only be found when A is the same type as B.
For example, this compiles:
scala> implicitly[Int =:= Int] res0: =:=[Int,Int] = <function1>
but this does not:
scala> implicitly[Int =:= String] error: could not find implicit value for parameter e: =:=[Int,String]
Also available are <:< and <%< for type conformance and views, respectively.
A conformance (<:<) example:
scala> implicitly[Int =:= AnyVal] error: could not find implicit value for parameter e: =:=[Int,AnyVal] scala> implicitly[Int <:< AnyVal] res1: <:<[Int,AnyVal] = <function1>
A conversion (<%<) example:
scala> implicitly[Int <:< Long] error: could not find implicit value for parameter e: <:<[Int,Long] scala> implicitly[Int <%< Long] res1: <%<[Int,Long] = <function1>
Note that when the compiler prints the <:< and <%< types, it doesn’t use infix notation. A small patch to the compiler can get us infix, however:
scala> implicitly[Int <:< Long] error: could not find implicit value for parameter e: (Int <:< Long) scala> implicitly[Int <%< Long] res1: (Int <%< Long) = <function1>
It has a bit of a hack to print full names if the simple name is ambiguous. After 2.8.0.final is out, I’ll try to get feedback to improve it enough to be included in the standard compiler.
Furthermore, combining implicit and default parameters you convert compile errors into a boolean value of false. One use case is for writing unit tests over type relationships, where you might like assert that
!(Set[Int] <:< Set[Any])
scala> def type_==[A, B](implicit ev: A =:= B = null) = ev ne null
type_$eq$eq: [A,B](implicit ev: =:=[A,B])Boolean
scala> type_==[Int, Int]
res0: Boolean = true
scala> type_==[Seq[Int], Seq[Short]]
res1: Boolean = false
It would be more interesting in the spirit of this series to evaluate the type relationship to a type encoded boolean (e.g BooleanType#TRUE), but I’m not sure this is possible.
Nice! Much better than writing:
// this doesn’t compile
You can get it to return True/False, but I don’t think it is useful, since you can’t use it in type-only computations.
Jason, that’s really cool. I feel we’re getting closer and closer to C++ MPL here, which is, arguably, a good thing. :)
A type level type equality check would be really useful as it’s last missing element to creating type sets (which basically are type lists + type equality check), which have some interesting applications. Unfortunately I haven’t found a way to do it in Scala, so if you figure out how to do it, be sure to let us know. :)
Stupid question: Are =:= and <:< types that could've been written by an user (but happen to be defined in Predef) our are they special compiler magic?
PS: I'm looking forward to the next episodes. Type level programming is always fun, and sometimes even useful!
Actually, it’s a really good question, to which the answer is yes.
Yes, they could be written by a user. See:
Pingback: Type-Level Programming in Scala « Apocalisp
Pingback: Type Level Programming: Equality « Michid’s Weblog
Revision-independent links to that Predef section: