Една от новите функции на Scala 2.8 са контекстните граници. Какво е обвързан контекст и къде е полезен?
Разбира се, първо потърсих (и намерих например това), но не можах да намеря наистина ясна и подробна информация.
Една от новите функции на Scala 2.8 са контекстните граници. Какво е обвързан контекст и къде е полезен?
Разбира се, първо потърсих (и намерих например това), но не можах да намеря наистина ясна и подробна информация.
Намерихте ли тази статия? Той обхваща новата функция, свързана с контекста, в контекста на подобренията на масива.
Обикновено тип параметър с обвързан контекст е във формата [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 <% B
) улавя концепцията за „може да се разглежда като“ (докато горната граница <:
улавя концепцията за „е a“). Обвързан контекст (A : C
) казва "има" за тип. Можете да прочетете примерите за манифестите като „T
има Manifest
“. Примерът, към който сте дали връзка за Ordered
срещу Ordering
, илюстрира разликата. Метод
def example[T <% Ordered[T]](param: T)
казва, че параметърът може да се види като Ordered
. Сравнете с
def example[T : Ordering](param: T)
което казва, че параметърът има свързано Ordering
.
По отношение на употребата отне известно време, за да бъдат установени конвенциите, но контекстните граници са за предпочитане пред границите на изгледа (границите на изгледа вече са отхвърлени). Едно предложение е, че контекстното обвързване е предпочитано, когато трябва да прехвърлите имплицитна дефиниция от един обхват в друг, без да е необходимо да се позовавате директно на него (това със сигурност е случаят с ClassManifest
, използван за създаване на масив).
Друг начин за мислене за границите на изгледа и границите на контекста е, че първият прехвърля неявни преобразувания от обхвата на повикващия. Вторият прехвърля неявни обекти от обхвата на повикващия.
has a
има повече смисъл за мен]
- 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
Това също може да бъде изразено с обвързване на контекста, с помощта на псевдоним на тип, представляващ функции от тип 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
From
от типа To[String]
. Ние не предоставяме аргумент тип на From
, така че се отнасяме към конструктора на типа, а не към типа. Този конструктор на типове е от правилния вид, за да се използва като обвързан с контекст -- * -> *
. Това ограничава параметъра на типа T
чрез изискване за неявен параметър от тип To[String]#From[T]
. Разширете псевдонимите на типа и готово, оставате с Function1[String, T]
.
- person retronym; 07.11.2010
Това е друга бележка в скоби.
Както Бен посочи, контекстното обвързване представлява "има-а" ограничение между параметър тип и клас тип. Казано по друг начин, това представлява ограничение, че съществува имплицитна стойност на конкретен тип клас.
Когато се използва обвързан контекст, често се налага да изведе на повърхността тази имплицитна стойност. Например, като се има предвид ограничението 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) }