Что такое "привязка к контексту" в Scala?

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

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

4 ответа

Решение

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

Обычно параметр типа с привязкой к контексту имеет вид [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
}

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

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

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

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

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

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

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

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

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

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

Итак, учитывая этот код, выраженный с помощью 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

Это также может быть выражено с помощью Context Bound с помощью псевдонима типа, представляющего функции из типа 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 

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

Как указал Бен, контекстная граница представляет ограничение "имеет-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) }
Другие вопросы по тегам