Что такое контекст Scala и границы представления?

StackOverflow https://stackoverflow.com/questions/4465948

  •  11-10-2019
  •  | 
  •  

Вопрос

Проще говоря, что такое границы контекста и представления и в чем разница между ними?

Некоторые простые примеры тоже были бы отличными!

Это было полезно?

Решение

Я думал, что это уже было задано, но, если так, вопрос не очевиден в «связанной» баре. Итак, вот это:

Что такое вид?

А Посмотреть связанного был механизм, введенный в 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, чтобы получить больше знакомых всех способов, которыми они могут быть использованы.

РЕДАКТИРОВАТЬ

Связанные вопросы, представляющие интерес:

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top