Я думал, что это уже задавали, но, если это так, вопрос не виден в "связанной" панели. Итак, вот оно:
Что такое граница вида?
A view bound был механизм, введенный в Scala, чтобы позволить использовать некоторый тип A
, как если бы это был некоторый тип B
. Типичный синтаксис такой:
def f[A <% B](a: A) = a.bMethod
Другими словами, A
должен иметь неявное преобразование в B
, чтобы можно было вызывать методы B
для объекта типа A
. Наиболее распространенное использование границ представлений в стандартной библиотеке (во всяком случае, до Scala 2.8.0) - Ordered
, например:
def f[A <% Ordered[A]](a: A, b: A) = if (a < b) a else b
Поскольку можно преобразовать A
в Ordered[A]
, а поскольку Ordered[A]
определяет метод <(other: A): Boolean
, я могу использовать выражение a < b
.
Обратите внимание, что вид границ устарел , их следует избегать.
Что такое контекст, связанный?
Ограничения контекста были введены в Scala 2.8.0 и обычно используются с так называемым шаблоном класса , шаблоном кода, который имитирует функциональность, предоставляемую классами типов Haskell, хотя в более многословная манера.
Хотя граница вида может использоваться с простыми типами (например, A <% String
), для границы контекста требуется параметризованный тип , такой как Ordered[A]
выше, но в отличие от String
.
Ограничение контекста описывает неявное значение вместо неявного преобразования границы представления . Он используется для объявления того, что для некоторого типа A
имеется неявное значение типа B[A]
. Синтаксис выглядит так:
def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A]
Это более запутанно, чем связанный вид, потому что не сразу понятно, как его использовать. Типичный пример использования в Scala:
def f[A : ClassManifest](n: Int) = new Array[A](n)
Инициализация Array
для параметризованного типа требует наличия ClassManifest
по загадочным причинам, связанным с стиранием типов и природой массивов без стирания.
Еще один очень распространенный пример в библиотеке немного сложнее:
def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)
Здесь implicitly
используется для извлечения неявного значения, которое мы хотим, одного типа Ordering[A]
, класс которого определяет метод compare(a: A, b: A): Int
.
Ниже мы увидим другой способ сделать это.
Как реализованы границы просмотра и границы контекста?
Не удивительно, что как границы представления, так и границы контекста реализованы с неявными параметрами, учитывая их определение. Фактически, синтаксис, который я показал, является синтаксическим сахаром для того, что действительно происходит. Смотрите ниже, как они удаляют сахар:
def f[A <% B](a: A) = a.bMethod
def f[A](a: A)(implicit ev: A => B) = a.bMethod
def g[A : B](a: A) = h(a)
def g[A](a: A)(implicit ev: B[A]) = h(a)
Итак, естественно, их можно написать в их полном синтаксисе, что особенно полезно для границ контекста:
def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)
Для чего используются границы просмотра?
Границы представления используются в основном для использования шаблона pimp my library , с помощью которого «добавляются» методы к существующему классу в ситуациях, когда вы хотите каким-либо образом вернуть исходный тип. Если вам не нужно возвращать этот тип каким-либо образом, тогда вам не нужно привязывать представление.
Классическим примером использования привязки к представлению является обработка Ordered
. Обратите внимание, что Int
не является, например, Ordered
, хотя существует неявное преобразование. Приведенный выше пример требует привязки к виду, поскольку он возвращает неконвертированный тип:
def f[A <% Ordered[A]](a: A, b: A): A = if (a < b) a else b
Этот пример не будет работать без границ просмотра. Однако, если бы я должен был вернуть другой тип, мне больше не нужно привязывать представление:
def f[A](a: Ordered[A], b: A): Boolean = a < b
Преобразование здесь (при необходимости) происходит до того, как я передаю параметр в f
, поэтому f
не нужно знать об этом.
Помимо Ordered
, наиболее распространенное использование из библиотеки - обработка String
и Array
, которые являются классами Java, как и коллекции Scala. Например:
def f[CC <% Traversable[_]](a: CC, b: CC): CC = if (a.size < b.size) a else b
Если попытаться сделать это без границ представления, тип возврата String
будет WrappedString
(Scala 2.8), и аналогично для Array
.
То же самое происходит, даже если тип используется только как параметр типа возвращаемого типа:
def f[A <% Ordered[A]](xs: A*): Seq[A] = xs.toSeq.sorted
Для чего используются границы контекста?
Границы контекста в основном используются в том, что стало известно как шаблон класса типов , как ссылка на классы типов Haskell.По сути, этот шаблон реализует альтернативу наследованию, делая функциональность доступной через неявный шаблон адаптера.
Классическим примером является Scala 2.8 Ordering
, который заменил Ordered
во всей библиотеке Scala.Используется:
def f[A : Ordering](a: A, b: A) = if (implicitly[Ordering[A]].lt(a, b)) a else b
Хотя вы обычно увидите, что написано так:
def f[A](a: A, b: A)(implicit ord: Ordering[A]) = {
import ord.mkOrderingOps
if (a < b) a else b
}
, которые используют некоторые неявные преобразования внутри Ordering
, которые включают традиционный стиль оператора,Другой пример в Scala 2.8 - это Numeric
:
def f[A : Numeric](a: A, b: A) = implicitly[Numeric[A]].plus(a, b)
Более сложный пример - использование новой коллекции CanBuildFrom
, но об этом уже есть очень длинный ответ, поэтому я буду избегать егоВот.И, как упоминалось ранее, есть использование ClassManifest
, которое требуется для инициализации новых массивов без конкретных типов.
Контекст, связанный с шаблоном класса типов, гораздо чаще используется вашими собственными классами, так какони позволяют разделить задачи, в то время как в вашем собственном коде можно избежать границ представлений благодаря хорошему дизайну (он используется в основном для обхода чужого дизайна).
Хотя это было возможно в течение длительного времени, использованиеграницы контекста действительно исчезли в 2010 году, и в настоящее время обнаруживаются в некоторой степени в большинстве наиболее важных библиотек и сред Scala.Самым ярким примером его использования, однако, является библиотека Scalaz, которая дает большую часть возможностей Haskell для Scala.Я рекомендую ознакомиться с образцами классов типов, чтобы больше узнать обо всех способах их использования.
РЕДАКТИРОВАТЬ
Смежные вопросы, представляющие интерес: