Що таке контекст Scala та межа перегляду?


267

Простим способом, що таке межі контексту та перегляду та яка різниця між ними?

Деякі легкі для наслідування приклади теж будуть чудовими!

Відповіді:


477

Я думав, що це вже задавали, але, якщо так, то питання не видно в панелі "пов'язаних". Отже, ось це:

Що таке перегляд меж?

Обмежений вигляд був механізмом, запровадженим у Scala, щоб дозволити використовувати якийсь тип, A як ніби якийсь тип B. Типовий синтаксис такий:

def f[A <% B](a: A) = a.bMethod

Іншими словами, Aповинно бути неявне перетворення у Bдоступне, щоб можна було викликати Bметоди на об'єкті типу A. Найбільш поширене використання меж перегляду в стандартній бібліотеці (так само раніше, ніж Scala 2.8.0) - це з Orderedтаким:

def f[A <% Ordered[A]](a: A, b: A) = if (a < b) a else b

Оскільки можна перетворити Aв an Ordered[A], і тому, що Ordered[A]визначає метод <(other: A): Boolean, я можу використовувати вираз a < b.

Зауважте, що межі перегляду застарілі , ви повинні їх уникати.

Що таке обмеження контексту?

Межі контексту були введені в Scala 2.8.0 і зазвичай використовуються з так званим шаблоном класового типу , зразком коду, який імітує функціональність, що надається класами типу Haskell, хоча і більш докладно.

Хоча обмежений вигляд може бути використаний з простими типами (наприклад, A <% String), обмеження контексту вимагає параметризованого типу , такого як Ordered[A]вище, але на відміну від цього String.

Обмежений контекст описує неявне значення замість неявного перетворення зв'язаного перегляду . Він використовується, щоб оголосити, що для певного Aтипу B[A]наявне неявне значення типу . Синтаксис виглядає так:

def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A]

Це більш заплутано, ніж зв'язаний погляд, оскільки не відразу зрозуміло, як ним користуватися. Найпоширеніший приклад використання в Scala:

def f[A : ClassManifest](n: Int) = new Array[A](n)

ArrayІніціалізації на параметризрвані типі вимагає , ClassManifestщоб бути доступними, таємними причини , пов'язаних з типом стиранням і без стирання природи масивів.

Ще один дуже поширений приклад у бібліотеці - трохи складніший:

def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)

Тут implicitlyвикористовується для отримання потрібного нам неявного значення, одного з типів Ordering[A], який клас визначає методом compare(a: A, b: A): Int.

Ми побачимо інший спосіб зробити це нижче.

Як реалізуються межі перегляду та межі контексту?

Не дивно, що і межі перегляду, і рамки контексту реалізуються з неявними параметрами, враховуючи їх визначення. Власне, синтаксис, який я показав, - це синтаксичні цукри для того, що відбувається насправді. Нижче дивіться, як вони знежирюють цукор:

def f[A <% B](a: A) = a.bMethod
def f[A](a: A)(implicit ev: A => B) = a.bMethod

def g[A : B](a: A) = h(a)
def g[A](a: A)(implicit ev: B[A]) = h(a)

Отже, природно, можна записати їх у повному синтаксисі, що особливо корисно для контекстних меж:

def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)

Для чого використовуються оглядові межі?

Межі перегляду в основному використовуються для того, щоб скористатися шаблоном сутенера моєї бібліотеки , за допомогою якого методи "додає" до наявного класу в ситуаціях, коли ви хочете якось повернути початковий тип. Якщо вам ні в якому разі не потрібно повертати цей тип, то вам не потрібно обмежувати перегляд.

Класичний приклад використання обмеженого перегляду - обробка Ordered. Зверніть увагу, що Intце не є Ordered, наприклад, хоча є неявна конверсія. Раніше наведений приклад потребує перегляду, оскільки він повертає неперетворений тип:

def f[A <% Ordered[A]](a: A, b: A): A = if (a < b) a else b

Цей приклад не буде працювати без меж перегляду. Однак, якби я повертав інший тип, мені більше не потрібен перегляд:

def f[A](a: Ordered[A], b: A): Boolean = a < b

Перетворення тут (якщо потрібно) відбувається до того, як я передам параметр f, тому fне потрібно про це знати.

Крім того Ordered, найпоширенішим використанням бібліотеки є обробка Stringта Array, які є класами Java, як і колекції Scala. Наприклад:

def f[CC <% Traversable[_]](a: CC, b: CC): CC = if (a.size < b.size) a else b

Якщо хтось намагався зробити це без меж перегляду, то тип повернення a Stringбуде " WrappedString(Scala 2.8)" і подібним чином для Array.

Те ж саме відбувається, навіть якщо тип використовується лише як параметр типу повернення типу:

def f[A <% Ordered[A]](xs: A*): Seq[A] = xs.toSeq.sorted

Для чого використовуються контекстні межі?

Межі контексту в основному використовуються в тому, що стало відомим як шаблон типу класів, як посилання на типи класів Haskell. В основному, ця модель реалізує альтернативу успадкуванню, роблячи доступність функціональних можливостей через якусь неявну схему адаптера.

Класичний приклад - Scala 2.8 Ordering, який замінив Orderedвсю бібліотеку Scala. Використання:

def f[A : Ordering](a: A, b: A) = if (implicitly[Ordering[A]].lt(a, b)) a else b

Хоча ви зазвичай бачите, що написано так:

def f[A](a: A, b: A)(implicit ord: Ordering[A]) = {
    import ord.mkOrderingOps
    if (a < b) a else b
}

Які користуються деякими неявними перетвореннями всередині, Orderingщо дозволяють використовувати традиційний стиль оператора. Іншим прикладом у Scala 2.8 є Numeric:

def f[A : Numeric](a: A, b: A) = implicitly[Numeric[A]].plus(a, b)

Складнішим прикладом є використання нової колекції CanBuildFrom, але відповідь на це вже дуже довгий, тому я тут цього уникатиму. І, як згадувалося раніше, є ClassManifestвикористання, яке потрібно для ініціалізації нових масивів без конкретних типів.

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

Хоча це вже давно можливо, використання контекстних меж справді було знято в 2010 році, і зараз воно певною мірою зустрічається у більшості найважливіших бібліотек та рамок Scala. Найбільш крайнім прикладом його використання є бібліотека Скалаз, яка приносить багато сил Хаскелла Скалі. Я рекомендую ознайомитись з шаблонами типу, щоб ознайомитись з усіма способами їх використання.

EDIT

Супутні питання, що цікавлять:


9
Дуже дякую. Я знаю, що на це відповіли і раніше, і, можливо, я тоді не читав достатньо уважно, але ваше пояснення тут є найбільш зрозумілим, що я бачив. Тож ще раз дякую.
chrsan

3
@chrsan Я додав ще два розділи, детальніше розповідаючи про те, де використовується кожен з них.
Даніель К. Собрал

2
Я думаю, що це відмінне пояснення. Я хотів би перекласти це для свого німецького блогу (dgronau.wordpress.com), якщо з вами це нормально.
Ландей

3
Це, безумовно, найкраще та найповніше пояснення цієї теми, яке я знайшов поки що. Велике спасибі, дійсно!
fotNelton

2
Ооо, коли вийде твоя книга Scala і де її можна придбати :)
wfbarksdale
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.