Это так называемые ограничения обобщенных типов . Они позволяют вам изнутри параметризованного типа класса или признака дополнительно ограничить один из его параметров типа. Вот пример:
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]
. По сути, вы пишете специальный код для определенных видов списков.