Відповіді:
Я думав, що це вже задавали, але, якщо так, то питання не видно в панелі "пов'язаних". Отже, ось це:
Обмежений вигляд був механізмом, запровадженим у 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
Супутні питання, що цікавлять: