Что такое «привязка к контексту» в Scala? - PullRequest
104 голосов
/ 06 июня 2010

Одной из новых функций Scala 2.8 являются границы контекста. Что такое контекст, связанный и где он полезен?

Конечно, я сначала искал (и нашел, например, это ), но я не смог найти действительно четкой и подробной информации.

Ответы [ 4 ]

138 голосов
/ 06 июня 2010

Ответ Роберта охватывает технические подробности границ контекста. Я дам вам свое толкование их значения.

В Scala View Bound (A <% B) фиксирует концепцию «можно увидеть как» (тогда как верхняя граница <: фиксирует концепцию «является»). Ограниченная контекстом (A : C) говорит «имеет» о типе. Вы можете прочитать примеры о манифестах как "T имеет Manifest". Пример, на который вы ссылаетесь примерно на Ordered против Ordering, иллюстрирует разницу. Метод

def example[T <% Ordered[T]](param: T)

говорит, что параметр можно рассматривать как Ordered. Сравните с

def example[T : Ordering](param: T)

, который говорит, что с параметром связан Ordering.

С точки зрения использования, для создания соглашений потребовалось некоторое время, но границы контекста предпочтительнее границ представления ( границы представления теперь устарели ). Одно из предположений заключается в том, что привязка к контексту предпочтительна, когда вам нужно перенести неявное определение из одной области в другую, не обращаясь к нему напрямую (это, безусловно, относится к ClassManifest, используемому для создания массива).

Еще один способ рассмотрения границ представления и границ контекста состоит в том, что первый переносит неявные преобразования из области видимости вызывающего. Вторая передает неявные объекты из области видимости вызывающего.

99 голосов
/ 06 июня 2010

Вы нашли эту статью ?Он охватывает новую функцию, связанную с контекстом, в контексте улучшений массива.

Обычно параметр типа с ограничением контекста имеет вид [T: Bound];он расширяется до параметра простого типа T вместе с неявным параметром типа Bound[T].

Рассмотрим метод tabulate, который формирует массив из результатов применения данной функции f в диапазоне чисел от 0 до заданной длины.До Scala 2.7 табулирование могло быть записано следующим образом:

def tabulate[T](len: Int, f: Int => T) = {
    val xs = new Array[T](len)
    for (i <- 0 until len) xs(i) = f(i)
    xs
}

В Scala 2.8 это больше невозможно, поскольку информация времени выполнения необходима для создания правильного представления Array[T].Нужно предоставить эту информацию, передав ClassManifest[T] в метод в качестве неявного параметра:

def tabulate[T](len: Int, f: Int => T)(implicit m: ClassManifest[T]) = {
    val xs = new Array[T](len)
    for (i <- 0 until len) xs(i) = f(i)
    xs
}

В качестве сокращенной формы можно использовать привязанный к контексту для параметра типаT вместо этого, давая:

def tabulate[T: ClassManifest](len: Int, f: Int => T) = {
    val xs = new Array[T](len)
    for (i <- 0 until len) xs(i) = f(i)
    xs
}
38 голосов
/ 06 июня 2010

(Это примечание в скобках. Сначала прочтите и поймите другие ответы.)

Границы контекста фактически обобщают границы представления.

Итак, учитывая этот код, выраженный с помощью View Bound:

scala> implicit def int2str(i: Int): String = i.toString
int2str: (i: Int)String

scala> def f1[T <% String](t: T) = 0
f1: [T](t: T)(implicit evidence$1: (T) => String)Int

Это также может быть выражено с помощью привязки контекста с помощью псевдонима типа, представляющего функции от типа F до типа T.

scala> trait To[T] { type From[F] = F => T }           
defined trait To

scala> def f2[T : To[String]#From](t: T) = 0       
f2: [T](t: T)(implicit evidence$1: (T) => java.lang.String)Int

scala> f2(1)
res1: Int = 0

Контекстная граница должна использоваться с конструктором типа * => *. Однако конструктор типа Function1 имеет вид (*, *) => *. Использование псевдонима типа частично применяет параметр второго типа с типом String, давая конструктор типа правильного вида для использования в качестве границы контекста.

Существует предложение разрешить вам напрямую выражать частично примененные типы в Scala без использования псевдонима типа внутри черты. Вы могли бы тогда написать:

def f3[T : [X](X => String)](t: T) = 0 
16 голосов
/ 18 декабря 2010

Это еще одно примечание в скобках.

Как указал Бен , граница контекста представляет ограничение "has-a" между параметром типа и классом типа. Другими словами, он представляет собой ограничение на существование неявного значения определенного типа.

При использовании привязки к контексту часто нужно вспомнить это неявное значение. Например, учитывая ограничение T : Ordering, часто требуется экземпляр Ordering[T], который удовлетворяет ограничению. Как показано здесь , можно получить неявное значение, используя метод implicitly или чуть более полезный метод context:

def **[T : Numeric](xs: Iterable[T], ys: Iterable[T]) = 
   xs zip ys map { t => implicitly[Numeric[T]].times(t._1, t._2) }

или

def **[T : Numeric](xs: Iterable[T], ys: Iterable[T]) =
   xs zip ys map { t => context[T]().times(t._1, t._2) }
...