I can see in the API docs for Predef that they’re subclasses of a generic function type (From) => To, but that’s all it says. Um, what? Maybe there’s documentation somewhere, but search engines don’t handle “names” like “<:<” very well, so I haven’t been able to find it.
Follow-up question: when should I use these funky symbols/classes, and why?
These are called generalized type constraints. They allow you, from within a type-parameterized class or trait, to further constrain one of its type parameters. Here’s an example:
The implicit argument
evidenceis supplied by the compiler, iffAisString. You can think of it as a proof thatAisString–the argument itself isn’t important, only knowing that it exists. [edit: well, technically it actually is important because it represents an implicit conversion fromAtoString, which is what allows you to calla.lengthand not have the compiler yell at you]Now I can use it like so:
But if I tried use it with a
Foocontaining something other than aString:You can read that error as “could not find evidence that Int == String”… that’s as it should be!
getStringLengthis imposing further restrictions on the type ofAthan whatFooin general requires; namely, you can only invokegetStringLengthon aFoo[String]. This constraint is enforced at compile-time, which is cool!<:<and<%<work similarly, but with slight variations:A =:= Bmeans A must be exactly BA <:< Bmeans A must be a subtype of B (analogous to the simple type constraint<:)A <%< Bmeans A must be viewable as B, possibly via implicit conversion (analogous to the simple type constraint<%)This snippet by @retronym is a good explanation of how this sort of thing used to be accomplished and how generalized type constraints make it easier now.
ADDENDUM
To answer your follow-up question, admittedly the example I gave is pretty contrived and not obviously useful. But imagine using it to define something like a
List.sumIntsmethod, which adds up a list of integers. You don’t want to allow this method to be invoked on any oldList, just aList[Int]. However theListtype constructor can’t be so constrainted; you still want to be able to have lists of strings, foos, bars, and whatnots. So by placing a generalized type constraint onsumInts, you can ensure that just that method has an additional constraint that it can only be used on aList[Int]. Essentially you’re writing special-case code for certain kinds of lists.