Какво е обвързан контекст в Scala?

Една от новите функции на Scala 2.8 са контекстните граници. Какво е обвързан контекст и къде е полезен?

Разбира се, първо потърсих (и намерих например това), но не можах да намеря наистина ясна и подробна информация.


person Jesper    schedule 05.06.2010    source източник
comment
вижте и това за обиколка на всички видове граници: gist.github.com/257758/47f06f2f3ca47702b 3a86c76a5479d096cb8c7ec   -  person Arjan Blokzijl    schedule 06.06.2010
comment
Този отличен отговор сравнява/сравнява границите на контекста и границите на изгледа: stackoverflow.com/questions/4465948/   -  person Aaron Novstrup    schedule 18.12.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 границата на изгледа (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, използван за създаване на масив).

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

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 често се използва в обектно-ориентирания дизайн, за да опише взаимоотношения между обекти (напр. Клиентът има адрес). Но тук връзката е между типове, а не между обекти. Това е свободна аналогия, защото връзката has не е присъща или универсална, както е в OO дизайна; клиентът винаги има адрес, но за обвързания с контекст A не винаги има C. По-скоро обвързаният с контекста указва, че екземпляр на C[A] трябва да бъде осигурен имплицитно. - person jbyler; 15.04.2016
comment
Уча Scala от един месец и това е най-доброто обяснение, което съм виждал този месец! Благодаря ти @Ben! - person Lifu Huang; 14.09.2016
comment
@Ben Lings: Благодаря, след като прекарахте толкова дълго време, за да разберете какво е обвързано с контекста, отговорът ви е много полезен.[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 
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