scalabook

Форк
0
183 строки · 8.6 Кб

Экземпляры given

Given instances (или просто "givens") определяют "канонические" значения определенных типов, которые служат для синтеза аргументов в параметрах контекста. Пример:

trait Ord[T]:
def compare(x: T, y: T): Int
extension (x: T) def < (y: T) = compare(x, y) < 0
extension (x: T) def > (y: T) = compare(x, y) > 0
given intOrd: Ord[Int] with
def compare(x: Int, y: Int) =
if x < y then -1 else if x > y then +1 else 0
given listOrd[T](using ord: Ord[T]): Ord[List[T]] with
def compare(xs: List[T], ys: List[T]): Int = (xs, ys) match
case (Nil, Nil) => 0
case (Nil, _) => -1
case (_, Nil) => +1
case (x :: xs1, y :: ys1) =>
val fst = ord.compare(x, y)
if fst != 0 then fst else compare(xs1, ys1)

Этот код определяет трейт Ord

с двумя экземплярами given
. intOrd
определяет given
для типа Ord[Int]
, тогда как listOrd[T]
определяет given
Ord[List[T]]
для всех типов T
, которые поставляются с given
экземпляром Ord[T]
. Предложение using
в listOrd
определяет условие: для существования given
типа Ord[List[T]]
должен существовать given
тип Ord[T]
. Такие условия расширяются компилятором до параметров контекста.

Результат:

def sort[T](lists: List[List[T]])(using ord: Ord[List[T]]): List[List[T]] =
lists.sortWith((l1, l2) => ord.compare(l1, l2) < 0)
sort(List(List(1, 3, 4), List(1, 2), List(1, 2, 3, 4, 5)))
// res0: List[List[Int]] = List(List(1, 2), List(1, 2, 3, 4, 5), List(1, 3, 4))

Анонимные givens

Имя given

может быть опущено. Таким образом, определения выше также могут быть выражены следующим образом:

given Ord[Int] with
...
given [T](using Ord[T]): Ord[List[T]] with
...

Если имя given

отсутствует, компилятор синтезирует его из реализованного типа (типов).

Имя, синтезированное компилятором, выбирается читабельным и достаточно кратким. Например, два приведенных выше экземпляра получат имена: given_Ord_Int

и given_Ord_List
. Точные правила синтеза имен находятся здесь. Эти правила не гарантируют отсутствия конфликтов имен между экземплярами типов given
, которые "слишком похожи". Чтобы избежать конфликтов, можно использовать именованные экземпляры.

Для обеспечения надежной двоичной совместимости общедоступным библиотекам следует отдавать предпочтение именованным экземплярам.

Псевдоним given

Псевдоним можно использовать для определения экземпляра given

, равного некоторому выражению. Пример:

given global: ExecutionContext = ForkJoinPool()

Это создает given global

типа ExecutionContext
, который равен правой части ForkJoinPool()
. При первом запросе к global
создается новый ForkJoinPool
, который затем возвращается для всех обращений к global
. Эта операция является потокобезопасной.

Псевдоним также может быть анонимным, например:

given Position = enclosingTree.position
given (using config: Config): Factory = MemoizingFactory(config)

Псевдоним given

может иметь параметры типа и параметры контекста, как и любой другой given
, но он может реализовывать только один тип.

Given макросы

Псевдонимы given могут иметь модификаторы inline

и transparent
. Пример:

transparent inline given mkAnnotations[A, T]: Annotations[A, T] = ${
// код, создающий экземпляр подтипа Annotations
}

Так как mkAnnotations

- transparent
, тип приложения — это тип его правой части, которая может быть правильным подтипом объявленного типа результата Annotations[A, T]
.

Экземпляры given

могут иметь модификаторы inline
, но не иметь transparent
, так как их тип уже известен из подписи. Пример:

trait Show[T] {
inline def show(x: T): String
}
inline given Show[Foo] with {
/*transparent*/ inline def show(x: Foo): String = ${ ... }
}
def app =
// встраивает вызов метода `show` и удаляет вызов `given Show[Foo]`
summon[Show[Foo]].show(foo)

Обратите внимание, что встроенные методы в экземплярах given

могут быть transparent
.

Встраивание экземпляров given

не будет встраивать/дублировать реализацию given
, оно просто дополнит создание экземпляра. Это используется для устранения мертвого кода экземпляров given
, которые не используются после встраивания.

Привязанные к шаблону экземпляры given

Экземпляры given

также могут появляться в шаблонах. Пример:

for given Context <- applicationContexts do
pair match
case (ctx @ given Context, y) => ...

В первом фрагменте анонимные экземпляры given

для класса Context
устанавливаются путем перечисления applicationContexts
. Во втором фрагменте given
экземпляр Context
с именем ctx
устанавливается путем сопоставления с первой половиной селектора pair
.

В каждом случае экземпляр given

, привязанный к шаблону, состоит из given
и типа T
. Шаблон соответствует точно тем же селекторам, что и шаблон приписывания типа _: T
.

Отрицательные given

Специальный тип scala.util.NotGiven

реализует "отрицательный" поиск в неявном расширении.

Для любого типа запроса Q

, NotGiven[Q]
выполняется успешно тогда и только тогда, когда неявный поиск Q
терпит неудачу, например:

import scala.util.NotGiven
trait Tagged[A]
case class Foo[A](value: String)
object Foo:
given fooTagged[A](using Tagged[A]): Foo[A] = Foo("fooTagged is found")
given fooNotTagged[A](using NotGiven[Tagged[A]]): Foo[A] = Foo("fooNotTagged is found")
given Tagged[Int]()
summon[Foo[Int]].value
// res1: String = "fooTagged is found"
summon[Foo[String]].value
// res2: String = "fooNotTagged is found"

Ссылки:

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.