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

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

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


person Jesper    schedule 05.06.2010    source источник
comment
также ознакомьтесь с этим, чтобы ознакомиться со всеми типами границ: gist.github.com/257476ec5c8c8c8c8c08c8c8c8c8c8c8c8c8c8c8c8c8c8 а>   -  person Arjan Blokzijl    schedule 06.06.2010
comment
Это очень хороший ответ stackoverflow.com/a/25250693/1586965   -  person samthebest    schedule 12.08.2014


Ответы (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
}
person Robert Harvey    schedule 05.06.2010

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

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

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

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

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

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

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

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

person Ben Lings    schedule 06.06.2010
comment
имеет, а не есть или виден как было ключевым моментом для меня - не видел этого ни в каких других объяснениях. Наличие простой английской версии немного загадочных операторов / функций значительно упрощает понимание - спасибо! - person DNA; 21.04.2013
comment
@Ben Lings Что вы имеете в виду под .... "имеет" о типе ...? Что такое тип? - person jhegedus; 17.03.2014
comment
@jhegedus Вот мой анализ: о типе означает, что A относится к типу. Фраза имеет часто используется в объектно-ориентированном дизайне для описания отношений между объектами (например, у клиента есть адрес). Но здесь существует связь между типами, а не объектами. Это вольная аналогия, потому что отношения has не являются неотъемлемыми или универсальными, как в объектно-ориентированном дизайне; У клиента всегда есть адрес, но для привязанного к контексту A не всегда имеет C. Скорее, привязка контекста указывает, что экземпляр C [A] должен быть предоставлен неявно. - person jbyler; 15.04.2016
comment
Я изучаю Scala в течение месяца, и это лучшее объяснение, которое я видел за этот месяц! Спасибо, @Ben! - person Lifu Huang; 14.09.2016
comment
@Ben Lings: Спасибо, после того, как вы потратили столько времени, чтобы понять, что связано с контекстом, ваш ответ очень полезен. [_ 1_ для меня имеет больше смысла] - person Shankar; 01.10.2016

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

Ограничения контекста фактически обобщают границы просмотра.

Итак, учитывая этот код, выраженный с помощью 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 
person retronym    schedule 06.06.2010
comment
Не могли бы вы объяснить значение #From в определении f2? Я не уверен, где строится тип F (правильно ли я сказал?) - person Collin; 07.11.2010
comment
Это называется проекцией типа, ссылаясь на член типа From типа To[String]. Мы не предоставляем аргумент типа для From, поэтому мы ссылаемся на конструктор типа, а не на тип. Этот конструктор типа подходит для использования в качестве привязки контекста - * -> *. Это ограничивает параметр типа T, требуя неявного параметра типа To[String]#From[T]. Разверните псевдонимы типов, и вуаля, у вас останется Function1[String, T]. - person retronym; 07.11.2010
comment
это должно быть Function1 [T, String]? - person ssanj; 29.01.2011

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

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

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

or

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