diff --git a/_ru/tour/implicit-conversions.md b/_ru/tour/implicit-conversions.md index 069c9be697..0ac21bd31f 100644 --- a/_ru/tour/implicit-conversions.md +++ b/_ru/tour/implicit-conversions.md @@ -8,51 +8,45 @@ next-page: polymorphic-methods previous-page: implicit-parameters --- -Неявные преобразование типа `S` к типу `T` задается неявным значением функционального типа `S =>T`, или неявным методом, который способен преобразовывать к значению требуемого типа. +Неявные преобразования — это мощная функция Scala, применяемая в двух распространенных вариантах: -Неявное преобразование применяются в двух случаях: +- разрешить пользователям предоставлять аргумент одного типа так, как если бы это был другой тип, чтобы избежать шаблонного. +- в Scala 2 для предоставления дополнительных членов запечатанным классам (заменены [методами расширения][exts] в Scala 3). -* Если выражение `e` типа `S` не подходит под ожидаемый тип выражения `T`. -* Если мы выбирая член `e.m`, где `e` является представителем типа `S`, при этом выбранное имя `m` не найдено среди доступных селекторов принадлежащих типу `S`. - -В первом случае выполняется поиск приведения `c`, которое можно применить к `e` чтоб тип результата стал соответствовать ожидаемому `T`. -Во втором случае выполняется поиск преобразования `c`, которое применимо к `e` и результат которого бы содержал член с именем `m`. +### Детальный разбор -Если неявный метод `List[A] => Ordered[List[A]]` находится в области видимости, также как и неявный метод `Int => Ordered[Int]`, то следующая операция с двумя списками типа `List[Int]` является допустимой: +{% tabs implicit-conversion-defn class=tabs-scala-version %} +{% tab 'Scala 2' %} -``` -List(1, 2, 3) <= List(4, 5) -``` +В Scala 2 неявное преобразование из типа `S` в тип `T` определяется либо [неявным классом]({% link _overviews/core/implicit-classes.md %}) `T` +с одним параметром типа `S`, [неявным значением]({% link _tour/implicit-parameters.md %}), которое имеет тип функции `S => T`, +либо неявным методом, преобразуемым в значение этого типа. -Неявный метод `Int => Ordered[Int]` предоставляется автоматически через `scala.Predef.intWrapper`. Ниже приведен пример объявления неявного метода `List[A] => Ordered[List[A]]`. +{% endtab %} +{% tab 'Scala 3' %} -```scala mdoc -import scala.language.implicitConversions +В Scala 3 неявное преобразование из типа `S` в тип `T` определяется [экземпляром `given`]({% link _tour/implicit-parameters.md %}), который имеет тип `scala.Conversion[S, T]`. +Для совместимости со Scala 2 их также можно определить неявным методом (подробнее читайте во вкладке Scala 2). -implicit def list2ordered[A](x: List[A]) - (implicit elem2ordered: A => Ordered[A]): Ordered[List[A]] = - new Ordered[List[A]] { - //заменить на более полезную реализацию - def compare(that: List[A]): Int = 1 - } -``` +{% endtab %} +{% endtabs %} -Неявно импортируемый объект `scala.Predef` объявляет ряд псевдонимов для часто используемым типов (например, `scala.collection.immutable.Map` использует псевдоним `Map`) и методов (например, `assert`), а также делает доступным целую серию неявных преобразований. +Неявные преобразования применяются в двух случаях: -Например, при вызове Java метода, который ожидает `java.lang.Integer`, вместо него вы можете свободно использовать `scala.Int`. Потому что Predef включает в себя следующие неявные преобразования: +1. Если выражение `e` имеет тип `S` и `S` не соответствует ожидаемому типу выражения `T`. +2. При выборе `e.m`, где `e` типа `S`, если селектор `m` не указывает на элемент `S`. -```scala mdoc -import scala.language.implicitConversions +В первом случае ищется конверсия `c`, применимая к `e` и тип результата которой соответствует `T`. -implicit def int2Integer(x: Int): Integer = - Integer.valueOf(x) -``` +Примером является передача `scala.Int`, например `x`, методу, который ожидает `scala.Long`. +В этом случае вставляется неявное преобразование `Int.int2long(x)`. -Компилятор предупреждает при компиляции об обнаружении неявных преобразований, т.к. неявные преобразования могут иметь разные подводные камни (особенно если использовать их без разбора). +Во втором случае ищется преобразование `c`, применимое к `e` и результат которого содержит элемент с именем `m`. -Чтоб отключить предупреждения выполните одно из следующих действий: +Примером является сравнение двух строк `"foo" < "bar"`. +В этом случае `String` не имеет члена `<`, поэтому вставляется неявное преобразование `Predef.augmentString("foo") < "bar"` +(`scala.Predef` автоматически импортируется во все программы Scala). -* Импортируйте `scala.language.implicitConversions` в области видимости, где объявлены неявные преобразования. -* Вызывайте компилятор с ключом `-language:implicitConversions`. +Дополнительная литература: [Неявные преобразования (в книге Scala)]({% link _overviews/scala3-book/ca-implicit-conversions.md %}). -В таком случае при преобразовании компилятором не будет выдаваться никаких предупреждений. +[exts]: {% link _overviews/scala3-book/ca-extension-methods.md %} diff --git a/_ru/tour/implicit-parameters.md b/_ru/tour/implicit-parameters.md index a04382022e..14f11c299c 100644 --- a/_ru/tour/implicit-parameters.md +++ b/_ru/tour/implicit-parameters.md @@ -1,6 +1,6 @@ --- layout: tour -title: Неявные Параметры +title: Контекстные параметры, также известные, как неявные параметры partof: scala-tour num: 26 language: ru @@ -8,61 +8,104 @@ next-page: implicit-conversions previous-page: self-types --- -Метод может иметь список _неявных_ параметров, помеченный ключевым словом _implicit_ в начале списка параметров. Если параметры в этом списке не передаются как обычно, то Scala будет искать, где можно получить неявное значение требуемого типа, и если найдет, то передаст его автоматически. +Метод может иметь список _контекстных параметров_ (_contextual parameters_), +также называемых _неявными параметрами_ (_implicit parameters_) или, точнее, _имплицитами_ (_implicits_). +Списки параметров, начинающиеся с ключевого слова `using` (или `implicit` в Scala 2), задают контекстные параметры. +Если сторона вызова явно не предоставляет аргументы для таких параметров, +Scala будет искать неявно доступные `given` (или `implicit` в Scala 2) значения правильного типа. +Если можно найти подходящие значения, то они автоматически передаются. -Места, где Scala будет искать эти параметры, делятся на две категории: - -* Скала сначала будет искать неявные параметры, доступ к которым можно получить напрямую (без префикса) в месте вызова метода в котором запрошены неявные параметры. -* Затем он ищет членов, помеченных как implicit во всех объектах компаньонах, связанных с типом неявного параметра. +Лучше всего вначале показать это на небольшом примере. +Мы определяем интерфейс `Comparator[A]`, который может сравнивать элементы типа `A`, +и предоставляем две реализации для `Int`-ов и `String`-ов. +Затем мы определяем метод `max[A](x: A, y: A)`, который возвращает больший из двух аргументов. +Так как `x` и `y` имеют абстрактный тип, в общем случае мы не знаем, как их сравнивать, но можем запросить соответствующий компаратор. +Поскольку обычно для любого заданного типа существует канонический компаратор `A`, +то мы можем объявить их как _заданные_ (_given_) или _неявно_ (_implicitly_) доступные. -Более подробное руководство, о том где scala ищет неявные значения можно найти в [FAQ](/tutorials/FAQ/finding-implicits.html) +{% tabs implicits-comparator class=tabs-scala-version %} -В следующем примере мы определяем метод `sum`, который вычисляет сумму элементов списка, используя операции `add` и `unit` моноида. Обратите внимание, что неявные значения не могут находится выше уровнем. +{% tab 'Scala 2' for=implicits-comparator %} ```scala mdoc -abstract class Monoid[A] { - def add(x: A, y: A): A - def unit: A +trait Comparator[A] { + def compare(x: A, y: A): Int } -object ImplicitTest { - implicit val stringMonoid: Monoid[String] = new Monoid[String] { - def add(x: String, y: String): String = x concat y - def unit: String = "" +object Comparator { + implicit object IntComparator extends Comparator[Int] { + def compare(x: Int, y: Int): Int = Integer.compare(x, y) } - - implicit val intMonoid: Monoid[Int] = new Monoid[Int] { - def add(x: Int, y: Int): Int = x + y - def unit: Int = 0 - } - - def sum[A](xs: List[A])(implicit m: Monoid[A]): A = - if (xs.isEmpty) m.unit - else m.add(xs.head, sum(xs.tail)) - - def main(args: Array[String]): Unit = { - println(sum(List(1, 2, 3))) // использует intMonoid неявно - println(sum(List("a", "b", "c"))) // использует stringMonoid неявно + + implicit object StringComparator extends Comparator[String] { + def compare(x: String, y: String): Int = x.compareTo(y) } } + +def max[A](x: A, y: A)(implicit comparator: Comparator[A]): A = + if (comparator.compare(x, y) >= 0) x + else y + +println(max(10, 6)) // 10 +println(max("hello", "world")) // world +``` + +```scala mdoc:fail +// не компилируется: +println(max(false, true)) +// ^ +// error: could not find implicit value for parameter comparator: Comparator[Boolean] ``` -`Monoid` определяет здесь операцию под названием `add`, которая сочетает два элемента типа `A` и возвращает сумму типа `A`, операция `unit` позволяет вернуть отдельный (специфичный) элемент типа `A`. +Параметр `comparator` автоматически заполняется значением `Comparator.IntComparator` для `max(10, 6)` +и `Comparator.StringComparator` для `max("hello", "world")`. +Поскольку нельзя найти неявный `Comparator[Boolean]`, вызов `max(false, true)` не компилируется. -Чтобы показать, как работают неявные параметры, сначала определим моноиды `stringMonoid` и `intMonoid` для строк и целых чисел, соответственно. Ключевое слово `implicit` указывает на то, что этот объект может быть использован неявно. +{% endtab %} -Метод `sum` принимает `List[A]` и возвращает `A`, который берет начальное `A` из `unit` и объединяет каждое следующее `A` в списке используя `add` метод. Указание параметра `m` в качестве неявного параметра подразумевает, что `xs` параметр будет обеспечен тогда, когда при вызове параметра метода Scala сможет найти неявный `Monoid[A]` чтоб его передать в качестве параметра `m`. +{% tab 'Scala 3' for=implicits-comparator %} -В нашем `main` методе мы вызываем `sum` дважды и предоставляем только `xs` параметр. Теперь Scala будет искать неявное значение в указанных ранее областях видимости. Первый вызов `sum` проходит с использованием `List[Int]` в качестве `xs`, это означает, что элемент `A` имеет тип `Int`. Неявный список параметров с `m` опущен, поэтому Scala будет искать неявное значение типа `Monoid[Int]`. Первое правило поиска гласит +```scala +trait Comparator[A]: +def compare(x: A, y: A): Int -> Скала сначала будет искать неявные параметры, доступ к которым можно получить напрямую (без префикса) в месте вызова метода в котором запрошены неявные параметры. +object Comparator: +given Comparator[Int] with +def compare(x: Int, y: Int): Int = Integer.compare(x, y) -`intMonoid` - это задание неявного значения, доступ к которому можно получить непосредственно в `main`. Оно имеет подходящий тип, поэтому передается методу `sum` автоматически. +given Comparator[String] with +def compare(x: String, y: String): Int = x.compareTo(y) +end Comparator -Второй вызов `sum` проходит используя `List[String]`, что означает, что `A` - это `String`. Неявный поиск будет идти так же, как и в случае с `Int`, но на этот раз будет найден `stringMonoid`, и передан автоматически в качестве `m`. +def max[A](x: A, y: A)(using comparator: Comparator[A]): A = + if comparator.compare(x, y) >= 0 then x + else y -Программа выведет на экран +println(max(10, 6)) // 10 +println(max("hello", "world")) // world ``` -6 -abc + +```scala +// не компилируется: +println(max(false, true)) +-- Error: ---------------------------------------------------------------------- +1 |println(max(false, true)) + | ^ + |no given instance of type Comparator[Boolean] was found for parameter comparator of method max ``` + +Параметр `comparator` автоматически заполняется значением `given Comparator[Int]` для `max(10, 6)` +и `given Comparator[String]` для `max("hello", "world")`. +Поскольку нельзя найти `given Comparator[Boolean]`, вызов `max(false, true)` не компилируется. + +{% endtab %} + +{% endtabs %} + +Места, где Scala будет искать эти параметры, делятся на две категории: + +- Вначале Scala будет искать `given` параметры, доступ к которым можно получить напрямую (без префикса) в месте вызова `max`. +- Затем он ищет членов, помеченных как given/implicit во всех объектах компаньонах, + связанных с типом неявного параметра (например: `object Comparator` для типа-кандидата `Comparator[Int]`). + +Более подробное руководство, о том где scala ищет неявные значения можно найти в [FAQ](/tutorials/FAQ/finding-implicits.html)