Что такое контекст 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)

Другие вопросы по тегам