Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update tour implicits in ru #2887

Merged
merged 6 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 27 additions & 33 deletions _ru/tour/implicit-conversions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 %}
119 changes: 81 additions & 38 deletions _ru/tour/implicit-parameters.md
Original file line number Diff line number Diff line change
@@ -1,68 +1,111 @@
---
layout: tour
title: Неявные Параметры
title: Контекстные параметры, также известные, как неявные параметры
partof: scala-tour
num: 26
language: ru
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)
Loading