В документации API для Predef я вижу, что они являются подклассами универсального типа функции (From) => To, но это все, что они говорят. Хм что? Может быть, где-то есть документация, но поисковые системы не очень хорошо обрабатывают «имена», например «<: <», поэтому я не смог ее найти.
Дополнительный вопрос: когда я должен использовать эти классные символы / классы и почему?
typeclass
работу этих операторов? Пример:compare :: Ord a => a -> a -> Ordering
? Я пытаюсь понять эту концепцию Scala относительно ее аналога на Haskell.Ответы:
Это так называемые ограничения обобщенных типов . Они позволяют вам изнутри параметризованного типа класса или признака дополнительно ограничить один из его параметров типа. Вот пример:
Неявный аргумент
evidence
предоставляется компилятором, еслиA
естьString
. Вы можете думать о нем , как доказательство , чтоA
являетсяString
само по себе --the аргумент не имеет значения, только зная , что она существует. [править: технически это действительно важно, потому что представляет неявное преобразование изA
вString
, что позволяет вам вызывать,a.length
а компилятор не кричать на вас]Теперь я могу использовать это так:
Но если бы я попытался использовать его с
Foo
чем-то, кромеString
:Вы можете прочитать эту ошибку как "не удалось найти доказательства того, что Int == String" ... так и должно быть!
getStringLength
налагает дополнительные ограничения на тип того,A
чтоFoo
обычно требуется; а именно, вы можете ссылаться толькоgetStringLength
наFoo[String]
. Это ограничение применяется во время компиляции, и это здорово!<:<
и<%<
работают аналогично, но с небольшими вариациями:A =:= B
означает, что A должен быть точно BA <:< B
означает, что A должен быть подтипом B (аналогично ограничению простого типа<:
)A <%< B
означает, что A должен просматриваться как B, возможно, посредством неявного преобразования (аналогично ограничению простого типа<%
)Этот фрагмент @retronym является хорошим объяснением того, как такого рода вещи выполнялись раньше, и как обобщенные ограничения типов делают это проще.
ДОПОЛНЕНИЕ
Чтобы ответить на ваш дополнительный вопрос, по общему признанию, приведенный мною пример довольно надуманен и явно не полезен. Но представьте себе его использование для определения чего-то вроде
List.sumInts
метода, который складывает список целых чисел. Вы не хотите, чтобы этот метод вызывался для любого старогоList
, просто aList[Int]
. ОднакоList
конструктор типа не может быть таким ограниченным; вы все еще хотите иметь возможность иметь списки строк, foos, баров и еще много чего. Таким образом, установив ограничение обобщенного типаsumInts
, вы можете убедиться, что только у этого метода есть дополнительное ограничение, которое может использоваться только вList[Int]
. По сути, вы пишете специальный код для определенных видов списков.источник
Manifest
которых вы не упомянули.Manifest
is<:<
и>:>
only ..., поскольку OP упомянул ровно 3 разновидности обобщенных ограничений типов, я предполагаю, что это то, что его интересовало.class =:=[From, To] extends From => To
, что означает, что неявное значение типаFrom =:= To
на самом деле является неявным преобразованием изFrom
вTo
. Таким образом, принимая неявный параметр типа,A =:= String
вы говорите, что онA
может быть неявно преобразован вString
. Если вы изменили порядок и сделали неявный аргумент типаString =:= A
, он не сработает, потому что это будет неявное преобразование изString
вA
.From =:= To
в области видимости подразумевает, что у вас есть неявное преобразованиеFrom => To
, но импликация не выполняется в обратном направлении; имеющий неявное преобразованиеA => B
вовсе не означает , у вас есть экземплярA =:= B
.=:=
является запечатанным абстрактным классом, определенным вscala.Predef
, и имеет только один публично представленный экземпляр, который является неявным и имеет типA =:= A
. Таким образом, вы гарантировано, что неявное значение типаA =:= B
свидетельствует о том, чтоA
иB
равны.Не полный ответ (другие уже ответили на это), я просто хотел отметить следующее, что, возможно, помогает лучше понять синтаксис: как вы обычно используете эти «операторы», как, например, в примере с pelotom:
использует альтернативный инфиксный синтаксис Scala для операторов типов .
Таким образом,
A =:= String
это то же самое, что и=:=[A, String]
(и=:=
это просто класс или черта с причудливым названием). Обратите внимание, что этот синтаксис также работает с «обычными» классами, например, вы можете написать:как это:
Это похоже на два синтаксиса для вызовов методов: «нормальный» с
.
и()
и операторный синтаксис.источник
makes use of Scala's alternative infix syntax for type operators.
полностью упускает это объяснение, без которого все это не имеет смыслаПрочитайте другие ответы, чтобы понять, что это за конструкции. Вот когда вы должны их использовать. Вы используете их, когда вам нужно ограничить метод только для определенных типов.
Вот пример. Предположим, вы хотите определить однородную пару, например:
Теперь вы хотите добавить метод
smaller
, например так:Это работает только если
T
заказано. Вы можете ограничить весь класс:Но это кажется позором - может быть использование для класса, когда
T
не заказано. С помощью ограничения типа вы все равно можете определитьsmaller
метод:Можно создать экземпляр, скажем, a
Pair[File]
, если только вы не вызываетеsmaller
его.В случае
Option
, разработчики хотелиorNull
метод, хотя это не имеет смыслаOption[Int]
. Используя ограничение типа, все хорошо. Вы можете использоватьorNull
наOption[String]
, и вы можете сформироватьOption[Int]
и использовать его, если вы не вызываетеorNull
его. Если вы попытаетесьSome(42).orNull
, вы получите очаровательное сообщениеисточник
<:<
, и я думаю, чтоOrdered
пример уже не так убедителен, так как теперь вы предпочитаете использоватьOrdering
класс типов, а неOrdered
черту. Что - то вроде:def smaller(implicit ord: Ordering[T]) = if (ord.lt(first, second)) first else second
.Это зависит от того, где они используются. Чаще всего, когда они используются при объявлении типов неявных параметров, они являются классами. Они могут быть объектами тоже в редких случаях. Наконец, они могут быть операторами на
Manifest
объектах. Они определены внутриscala.Predef
в первых двух случаях, хотя и не особенно хорошо документированы.Они предназначены для того, чтобы обеспечить способ проверить отношения между классами, точно так же, как
<:
и<%
делать, в ситуациях, когда последний не может быть использован.Что касается вопроса «когда я должен их использовать?», Ответ - не стоит, если только вы не знаете, что должны. :-) РЕДАКТИРОВАТЬ : Хорошо, хорошо, вот несколько примеров из библиотеки. У
Either
вас есть:У
Option
вас есть:Вы найдете несколько других примеров в коллекциях.
источник
:-)
еще один из них? И я бы согласился, что ваш ответ на «Когда я должен их использовать?» относится ко многим вещам.