Что делать <: <, <% <, и =: = означает в Scala 2.8, а где они документированы?
-
26-09-2019 - |
Вопрос
Я вижу в документах API для Предеф что они подклассы родового типа функции (из) =>, но это все, что он говорит. Хм, что? Может быть, есть где-то документация, но поисковые системы не обрабатывают «имена», как «<: <» очень хорошо, поэтому я не смог его найти.
Последующий вопрос: когда я должен использовать эти функции / классы / классы, а почему?
Решение
Это называется Обобщенные ограничения типа. Отказ Они позволяют вам, из-за типового параметризованного класса или черта, чтобы дополнительно ограничить один из параметров его типа. Вот пример:
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
поставляется компилятором, IFF A
является String
. Отказ Вы можете думать об этом как об этом как доказательство тот A
является String
- Сам аргумент не важен, только зная, что он существует. править: ну, технически это на самом деле важно, потому что он представляет неявное преобразование от 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
означает, что должен быть подтип B (аналогично просто Тип ограничения<:
)A <%< B
означает, что должно быть посмотреть Как B, возможно, через неявное преобразование (аналогично простому ограничению типа<%
)
Этот фрагмент @ Retrorrymy - это хорошее объяснение того, как эта вещь используется, и насколько обобщенными ограничениями типа делают это легче сейчас.
Приложение
Чтобы ответить на ваш последующий вопрос, по общему признанию, пример, который я дал, довольно удушают и не явно полезны. Но представьте, используя его, чтобы определить что-то вроде List.sumInts
Способ, который добавляет список целых чисел. Вы не хотите, чтобы этот метод был вызван на любой старый List
, просто а List[Int]
. Отказ Однако List
Тип конструктор не может быть настолько ограничен; Вы все еще хотите иметь возможность иметь списки строк, фуро, баров и othorots. Поэтому, поместив обобщенный тип ограничения на sumInts
, вы можете убедиться, что Просто этот метод имеет дополнительное ограничение, которое его можно использовать только на List[Int]
. Отказ По сути, вы пишете специальный код для определенных видов списков.
Другие советы
Не полный ответ (другие уже ответили на это), я просто хотел отметить следующее, что может лучше понять синтаксис лучше: то, как обычно вы используете эти «операторы», как, например, в примере Pelotom:
def getStringLength(implicit evidence: A =:= String)
использует альтернативу Scala Инфикс синтаксис для операторов типа.
Так, A =:= String
такой же как =:=[A, String]
(и =:=
это просто класс или черта с неожиданным именем). Обратите внимание, что этот синтаксис также работает с «регулярными» классами, например, вы можете написать:
val a: Tuple2[Int, String] = (1, "one")
как это:
val a: Int Tuple2 String = (1, "one")
Это похоже на два синтаксиса для вызовов метода, «нормально» с .
и ()
и синтаксис оператора.
Прочитайте другие ответы, чтобы понять, что эти конструкции. Вот когда Вы должны использовать их. Вы используете их, когда вам нужно ограничить способ только для определенных типов.
Вот пример. Предположим, вы хотите определить однородную пару, как это:
class Pair[T](val first: T, val second: T)
Теперь вы хотите добавить метод smaller
, как это:
def smaller = if (first < second) first else second
Это только работает, если T
заказывается. Вы можете ограничить весь класс:
class Pair[T <: Ordered[T]](val first: T, val second: T)
Но это кажется стыдом - можно использовать для класса, когда T
не заказывается. С ограничением типа, вы все еще можете определить smaller
Метод:
def smaller(implicit ev: T <:< Ordered[T]) = if (first < second) first else second
Это нормально, чтобы мгновенное, скажем, Pair[File]
, до тех пор, пока вы не звоните smaller
в теме.
В случае Option
, Ремонт хотел orNull
метод, даже если это не имеет смысла для Option[Int]
. Отказ Используя ограничение типа, все хорошо. Ты можешь использовать orNull
на Option[String]
, и вы можете сформировать Option[Int]
и использовать его, пока вы не звоните orNull
в теме. Если вы попытаетесь Some(42).orNull
, Вы получаете очаровательное сообщение
error: Cannot prove that Null <:< Int
Это зависит от того, где они используются. Чаще всего при использовании при объявлении типов неявных параметров они являются классами. Они могут быть объекты тоже в редких случаях. Наконец, они могут быть операторами на Manifest
объекты. Они определены внутри scala.Predef
В первые два случая, хотя и не особенно хорошо документированы.
Они предназначены для того, чтобы обеспечить способ проверить отношения между классами, как <:
и <%
Делайте, в ситуациях, когда последний нельзя использовать.
Что касается вопроса «Когда я должен использовать их?», Ответ - это вы не должны, если вы не знаете, что вы должны. :-) РЕДАКТИРОВАТЬ: Хорошо, хорошо, вот несколько примеров из библиотеки. На Either
, у тебя есть:
/**
* Joins an <code>Either</code> through <code>Right</code>.
*/
def joinRight[A1 >: A, B1 >: B, C](implicit ev: B1 <:< Either[A1, C]): Either[A1, C] = this match {
case Left(a) => Left(a)
case Right(b) => b
}
/**
* Joins an <code>Either</code> through <code>Left</code>.
*/
def joinLeft[A1 >: A, B1 >: B, C](implicit ev: A1 <:< Either[C, B1]): Either[C, B1] = this match {
case Left(a) => a
case Right(b) => Right(b)
}
На Option
, у тебя есть:
def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse null
Вы найдете некоторые другие примеры на коллекциях.