Это так называемые ограничения обобщенных типов . Они позволяют вам изнутри параметризованного типа класса или признака дополнительно ограничить один из его параметров типа. Вот пример:
case class Foo[A](a:A) { // 'A' can be substituted with any type
// getStringLength can only be used if this is a Foo[String]
def getStringLength(implicit evidence: A =:= String) = a.length
}
Неявный аргумент evidenceпредоставляется компилятором, если Aесть String. Вы можете думать о нем , как доказательство , что Aявляется Stringсамо по себе --the аргумент не имеет значения, только зная , что она существует. [править: технически это действительно важно, потому что представляет неявное преобразование из Aв String, что позволяет вам вызывать, a.lengthа компилятор не кричать на вас]
Теперь я могу использовать это так:
scala> Foo("blah").getStringLength
res6: Int = 4
Но если бы я попытался использовать его с Fooчем-то, кроме String:
scala> Foo(123).getStringLength
<console>:9: error: could not find implicit value for parameter evidence: =:=[Int,String]
Вы можете прочитать эту ошибку как "не удалось найти доказательства того, что Int == String" ... так и должно быть! getStringLengthналагает дополнительные ограничения на тип того, Aчто Fooобычно требуется; а именно, вы можете ссылаться только getStringLengthна Foo[String]. Это ограничение применяется во время компиляции, и это здорово!
<:<и <%<работают аналогично, но с небольшими вариациями:
A =:= B означает, что A должен быть точно B
A <:< Bозначает, что A должен быть подтипом B (аналогично ограничению простого типа <:)
A <%< Bозначает, что A должен просматриваться как B, возможно, посредством неявного преобразования (аналогично ограничению простого типа <%)
Этот фрагмент @retronym является хорошим объяснением того, как такого рода вещи выполнялись раньше, и как обобщенные ограничения типов делают это проще.
ДОПОЛНЕНИЕ
Чтобы ответить на ваш дополнительный вопрос, по общему признанию, приведенный мною пример довольно надуманен и явно не полезен. Но представьте себе его использование для определения чего-то вроде List.sumIntsметода, который складывает список целых чисел. Вы не хотите, чтобы этот метод вызывался для любого старого List, просто a List[Int]. Однако Listконструктор типа не может быть таким ограниченным; вы все еще хотите иметь возможность иметь списки строк, foos, баров и еще много чего. Таким образом, установив ограничение обобщенного типа sumInts, вы можете убедиться, что только у этого метода есть дополнительное ограничение, которое может использоваться только в List[Int]. По сути, вы пишете специальный код для определенных видов списков.