Что такое контекст Scala и границы просмотра?
Проще говоря, каковы границы контекста и представления и в чем разница между ними?
Некоторые простые в следовании примеры тоже были бы великолепны!
2 ответа
Я думал, что это уже задавали, но, если так, вопрос не очевиден в "связанной" панели. Итак, вот оно:
Что такое вид с привязкой?
Граница представления была механизмом, введенным в 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)
Для чего используются View Bounds?
Границы представлений используются главным образом для того, чтобы воспользоваться преимуществом шаблона 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
по всей библиотеке Скала. Использование:
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. Я рекомендую ознакомиться с образцами классов типов, чтобы больше узнать обо всех способах их использования.
РЕДАКТИРОВАТЬ
Смежные вопросы, представляющие интерес:
Я пробовал несколько способов, но ни один из них не работал. Ниже показано, как я это исправил.
(1<=T) && (200>=T)