Однією з нових особливостей Scala 2.8 є межі контексту. Що стосується контексту і де він корисний?
Звичайно, я шукав спочатку (і знайшов, наприклад, це ), але не зміг знайти жодної дійсно чіткої та детальної інформації.
Однією з нових особливостей Scala 2.8 є межі контексту. Що стосується контексту і де він корисний?
Звичайно, я шукав спочатку (і знайшов, наприклад, це ), але не зміг знайти жодної дійсно чіткої та детальної інформації.
Відповіді:
Ви знайшли цю статтю ? Він охоплює нову функцію, пов'язану з контекстом, у контексті покращення масиву.
Як правило, параметр типу з 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
}
Відповідь Роберта охоплює основні деталі Меж контексту. Я дам вам моє тлумачення їх значення.
У Scala a View Bound ( A <% B
) фіксується поняття "можна розглядати як" (тоді як верхня межа <:
фіксує поняття "є a"). Контекст, пов'язаний ( A : C
), говорить "має" про тип. Ви можете прочитати приклади про маніфести як " T
має Manifest
". Приклад, з яким ви пов’язували про Ordered
vs, Ordering
ілюструє різницю. Метод
def example[T <% Ordered[T]](param: T)
говорить, що параметр можна розглядати як Ordered
. Порівняйте з
def example[T : Ordering](param: T)
який говорить, що параметр має асоційований Ordering
.
Що стосується використання, то для встановлення конвенцій знадобився певний час, але рамки контексту переважніші за межі перегляду ( межі перегляду тепер застарілі ). Одне із припущень полягає в тому, що обмежений контекст є кращим, коли вам потрібно перенести неявне визначення з однієї області в іншу, не потребуючи посилань на нього безпосередньо (це, безумовно, має місце для ClassManifest
використовуваного для створення масиву).
Інший спосіб роздуму про межі перегляду та межі контексту полягає в тому, що перший передає неявні перетворення з сфери виклику. Другий передає неявні об'єкти з області виклику.
has a
Має більше сенсу для мене]
(Це батьківська примітка. Спочатку прочитайте та зрозумійте інші відповіді.)
Межі контексту фактично узагальнюють видимі межі.
Отже, враховуючи цей код, виражений за допомогою межі перегляду:
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]
.
Це ще одна батьківська примітка.
Як зазначив Бен , обмежений контекст являє собою обмеження "має-має" між типом параметра і класом типу. По-іншому, це означає обмеження, що існує неявне значення певного класу типу.
Використовуючи обмежений контекст, часто потрібно викрити це неявне значення. Наприклад, з огляду на обмеження 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) }