Что такое контекст Scala и границы представления?
Вопрос
Проще говоря, что такое границы контекста и представления и в чем разница между ними?
Некоторые простые примеры тоже были бы отличными!
Решение
Я думал, что это уже было задано, но, если так, вопрос не очевиден в «связанной» баре. Итак, вот это:
Что такое вид?
А Посмотреть связанного был механизм, введенный в 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)
Для чего используются границы представления?
Видные границы используются в основном для использования преимущества сутенер моя библиотека шаблон, с помощью которого один «добавляет» методы в существующий класс, в ситуациях, когда вы хотите каким -то образом вернуть исходный тип. Если вам не нужно возвращать этот тип каким -либо образом, вам не нужно связать представление.
Классическим примером обзора использования является обработка 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
Для чего используются границы контекста?
Контекстные границы в основном используются в том, что стало известно как Типоксельный шаблон, как ссылка на классы типов Хаскелла. По сути, этот шаблон реализует альтернативу наследование, предоставляя функциональность, доступную через неявный шаблон адаптера.
Классический пример - 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
Использование, которое необходимо для инициализации новых массивов без конкретных типов.
Контекст, связанный с шаблоном Typeclass, гораздо чаще используется вашими собственными классами, поскольку они позволяют разделить проблемы, тогда как границы представлений можно избежать в вашем собственном коде с помощью хорошего дизайна (он используется в основном для обоснования чужой дизайна )
Хотя это было возможно в течение долгого времени, использование контекстных границ действительно снялось в 2010 году и в настоящее время обнаружено в большинстве наиболее важных библиотек и рамок Scala. Однако наиболее крайним примером его использования является библиотека Scalaz, которая приносит большую часть мощности Хаскелла в Скала. Я рекомендую прочитать шаблоны TypeClass, чтобы получить больше знакомых всех способов, которыми они могут быть использованы.
РЕДАКТИРОВАТЬ
Связанные вопросы, представляющие интерес: