Що таке "обмежений контекст" у Scala?


115

Однією з нових особливостей Scala 2.8 є межі контексту. Що стосується контексту і де він корисний?

Звичайно, я шукав спочатку (і знайшов, наприклад, це ), але не зміг знайти жодної дійсно чіткої та детальної інформації.


8
також перегляньте це, щоб пройти тур усіх типів меж: gist.github.com/257758/47f06f2f3ca47702b3a86c76a5479d096cb8c7ec
Arjan Blokzijl

2
Це відмінний відповідь порівнює / контрасти контексту оцінки та перегляду оцінок: stackoverflow.com/questions/4465948 / ...
Aaron Novstrup

Це дуже хороший відповідь stackoverflow.com/a/25250693/1586965
samthebest

Відповіді:


107

Ви знайшли цю статтю ? Він охоплює нову функцію, пов'язану з контекстом, у контексті покращення масиву.

Як правило, параметр типу з a пов'язаним контекстом має форму [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
}

145

Відповідь Роберта охоплює основні деталі Меж контексту. Я дам вам моє тлумачення їх значення.

У Scala a View Bound ( A <% B) фіксується поняття "можна розглядати як" (тоді як верхня межа <:фіксує поняття "є a"). Контекст, пов'язаний ( A : C), говорить "має" про тип. Ви можете прочитати приклади про маніфести як " Tмає Manifest". Приклад, з яким ви пов’язували про Orderedvs, Orderingілюструє різницю. Метод

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

говорить, що параметр можна розглядати як Ordered. Порівняйте з

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

який говорить, що параметр має асоційований Ordering .

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

Інший спосіб роздуму про межі перегляду та межі контексту полягає в тому, що перший передає неявні перетворення з сфери виклику. Другий передає неявні об'єкти з області виклику.


2
"має", а не "є" або "сприймається як" було для мене ключовим розумінням - не бачив цього в жодних інших поясненнях. Наявність простої англійської версії інакше трохи криптованих операторів / функцій значно полегшує засвоєння - дякую!
ДНК

1
@ Ben Lings Що ви маєте на увазі під .... "має" про тип ...? Що стосується типу ?
jhegedus

1
@jhegedus Ось мій аналіз: "про тип" означає, що "A" відноситься до типу. Фраза "має" часто використовується в об'єктно-орієнтованому дизайні для опису об'єктних відносин (наприклад, Замовник має "Адреса). Але тут відносини "має" є між типами, а не об'єктами. Це нещільна аналогія, оскільки відносини "має" не притаманні або універсальні таким, яким вони є в дизайні ОО; у Замовника завжди є Адреса, але для пов'язаного контексту A не завжди має C. Скоріше, обмежений контекст визначає, що екземпляр C [A] повинен бути наданий неявно.
jbyler

Я вже місяць вивчаю Scala, і це найкраще пояснення, яке я бачив у цьому місяці! Дякую @Ben!
Ліфу Хуан

@Ben Lings: Спасибі, витративши так багато часу, щоб зрозуміти, що пов'язане з контекстом, ваша відповідь дуже корисна. [ has aМає більше сенсу для мене]
Shankar

39

(Це батьківська примітка. Спочатку прочитайте та зрозумійте інші відповіді.)

Межі контексту фактично узагальнюють видимі межі.

Отже, враховуючи цей код, виражений за допомогою межі перегляду:

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 у визначенні f2? Я не впевнений, де будується тип F (я це правильно сказав?)
Колін

1
Це називається типовою проекцією, посилаючись на типовий член Fromтипу To[String]. Ми не надаємо аргумент типу From, тому ми посилаємося на конструктор типу, а не на тип. Цей тип конструктора потрібний для використання як обмежений контекст - * -> *. Це поєднує параметр типу T, вимагаючи неявного параметра типу To[String]#From[T]. Розкрийте псевдоніми типу та вуаля, вам залишилося Function1[String, T].
ретронім

це має бути Function1 [T, String]?
ssanj

18

Це ще одна батьківська примітка.

Як зазначив Бен , обмежений контекст являє собою обмеження "має-має" між типом параметра і класом типу. По-іншому, це означає обмеження, що існує неявне значення певного класу типу.

Використовуючи обмежений контекст, часто потрібно викрити це неявне значення. Наприклад, з огляду на обмеження 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) }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.