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