From 8647ee5f22c8e54fefba6222e7ad477ed62c8b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9A=D0=BE=D1=80=D1=81?= =?UTF-8?q?=D0=B0=D0=BA=D0=BE=D0=B2?= Date: Sat, 15 Jul 2023 10:50:04 +0300 Subject: [PATCH 1/4] Update compound-types.md in russian --- _ru/tour/compound-types.md | 71 +++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/_ru/tour/compound-types.md b/_ru/tour/compound-types.md index 5d0a49aa0e..876bbaf42a 100644 --- a/_ru/tour/compound-types.md +++ b/_ru/tour/compound-types.md @@ -8,13 +8,19 @@ next-page: self-types previous-page: abstract-type-members --- -Иногда необходимо выразить, то что тип объекта является подтипом нескольких других типов. В Scala это можно выразить с помощью *составных типов*, которые являются объединением нескольких типов объектов. +Иногда необходимо выразить, то что тип объекта является подтипом нескольких других типов. + +В Scala это можно выразить с помощью _типов пересечений_ (или _составных типов_ в Scala 2), +которые являются объединением нескольких типов объектов. Предположим, у нас есть два трейта: `Cloneable` и `Resetable`: +{% tabs compound-types_1 class=tabs-scala-version %} +{% tab 'Scala 2' for=compound-types_1 %} + ```scala mdoc trait Cloneable extends java.lang.Cloneable { - override def clone(): Cloneable = { + override def clone(): Cloneable = { // создает публичный метод 'clone' super.clone().asInstanceOf[Cloneable] } } @@ -23,9 +29,26 @@ trait Resetable { } ``` -Теперь предположим, что мы хотим написать функцию `cloneAndReset`, которая берет объект, клонирует его и сбрасывает (Reset) состояние исходного объекта: +{% endtab %} +{% tab 'Scala 3' for=compound-types_1 %} +```scala +trait Cloneable extends java.lang.Cloneable: + override def clone(): Cloneable = // создает публичный метод 'clone' + super.clone().asInstanceOf[Cloneable] +trait Resetable: + def reset: Unit ``` + +{% endtab %} +{% endtabs %} + +Теперь предположим, что мы хотим написать функцию `cloneAndReset`, которая берет объект, клонирует его и сбрасывает (Reset) состояние исходного объекта: + +{% tabs compound-types_2 class=tabs-scala-version %} +{% tab 'Scala 2' for=compound-types_2 %} + +```scala mdoc:fail def cloneAndReset(obj: ?): Cloneable = { val cloned = obj.clone() obj.reset @@ -33,17 +56,49 @@ def cloneAndReset(obj: ?): Cloneable = { } ``` -Возникает вопрос, какой тип параметр `obj` должна принимать наша объединённая функция. Если это `Cloneable`, то объект может использовать метод `clone`, но не `reset`; если это `Resetable` мы можем использовать метод `reset`, но нет операции `clone`. Чтобы избежать приведения типа в такой ситуации, мы можем указать, что тип `obj` является и `Cloneable`, и `Resetable`. Этот совместный тип в Scala записывается как: `Cloneable with Resetable`. +{% endtab %} +{% tab 'Scala 3' for=compound-types_2 %} + +```scala +def cloneAndReset(obj: ?): Cloneable = + val cloned = obj.clone() + obj.reset + cloned +``` + +{% endtab %} +{% endtabs %} + +Возникает вопрос, какой тип параметра `obj` должна принимать наша объединённая функция. Если это `Cloneable`, то объект может использовать метод `clone`, но не `reset`; если это `Resetable` мы можем использовать метод `reset`, но нет операции `clone`. Чтобы избежать приведения типа в такой ситуации, мы можем указать, что тип `obj` является и `Cloneable`, и `Resetable`. + +{% tabs compound-types_3 class=tabs-scala-version %} +{% tab 'Scala 2' for=compound-types_3 %} +Этот совместный тип в Scala записывается как: `Cloneable with Resetable`. Вот обновленная функция: -``` +```scala mdoc:fail def cloneAndReset(obj: Cloneable with Resetable): Cloneable = { //... } ``` -Составные типы могут состоять из нескольких типов объектов, и они могут содержать единый доработанный объект, в котором будут доработаны характеристики существующих членов объекта. -Общая форма записи: `A with B with C ... { доработанный объект }` +Обратите внимание, что у вас может быть более двух типов: `A with B with C with ...`. +Это означает то же самое, что и: `(...(A with B) with C) with ... )` + +{% endtab %} +{% tab 'Scala 3' for=compound-types_3 %} +Этот совместный тип в Scala записывается как: `Cloneable & Resetable`. + +Вот обновленная функция: + +```scala +def cloneAndReset(obj: Cloneable & Resetable): Cloneable = { + //... +} +``` -Пример использования таких доработок приведен на странице об [объединении классов с примесями](mixin-class-composition.html). +Обратите внимание, что у вас может быть более двух типов: `A & B & C & ...`. +`&` является ассоциативным, поэтому скобки могут быть добавлены вокруг любой части без изменения значения. +{% endtab %} +{% endtabs %} From 1459fa3edfe4c45e376296a39436de04e2393bd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9A=D0=BE=D1=80=D1=81?= =?UTF-8?q?=D0=B0=D0=BA=D0=BE=D0=B2?= Date: Thu, 10 Aug 2023 09:44:57 +0300 Subject: [PATCH 2/4] Add ca-given-imports.md in ru --- _overviews/scala3-book/ca-given-imports.md | 2 +- _ru/scala3/book/ca-context-bounds.md | 2 +- _ru/scala3/book/ca-given-imports.md | 54 ++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 _ru/scala3/book/ca-given-imports.md diff --git a/_overviews/scala3-book/ca-given-imports.md b/_overviews/scala3-book/ca-given-imports.md index 399ff6add0..97ddde3374 100644 --- a/_overviews/scala3-book/ca-given-imports.md +++ b/_overviews/scala3-book/ca-given-imports.md @@ -2,7 +2,7 @@ title: Given Imports type: section description: This page demonstrates how 'given' import statements work in Scala 3. -languages: [zh-cn] +languages: [ru, zh-cn] num: 63 previous-page: ca-context-bounds next-page: ca-type-classes diff --git a/_ru/scala3/book/ca-context-bounds.md b/_ru/scala3/book/ca-context-bounds.md index 17dbfedbc0..91c18c5101 100644 --- a/_ru/scala3/book/ca-context-bounds.md +++ b/_ru/scala3/book/ca-context-bounds.md @@ -9,7 +9,7 @@ description: В этой главе представлены контекстн language: ru num: 62 previous-page: ca-context-parameters -next-page: +next-page: ca-given-imports --- Во многих ситуациях имя [контекстного параметра]({% link _overviews/scala3-book/ca-context-parameters.md %}#context-parameters) diff --git a/_ru/scala3/book/ca-given-imports.md b/_ru/scala3/book/ca-given-imports.md new file mode 100644 index 0000000000..271d622b7e --- /dev/null +++ b/_ru/scala3/book/ca-given-imports.md @@ -0,0 +1,54 @@ +--- +layout: multipage-overview +title: Given импорты +scala3: true +partof: scala3-book +overview-name: "Scala 3 — Book" +type: section +description: На этой странице показано, как работают операторы импорта 'given' в Scala 3. +language: ru +num: 63 +previous-page: ca-context-bounds +next-page: ca-type-classes +versionSpecific: true +--- + +Для большей ясности, откуда берутся данные в текущей области видимости, +для импорта экземпляров `given` используется специальная форма оператора `import`. +Базовая форма показана в этом примере: + +```scala +object A: + class TC + given tc: TC = ??? + def f(using TC) = ??? + +object B: + import A.* // импорт всех не-given элементов + import A.given // импорт экземпляров given +``` + +В этом коде предложение `import A.*` объекта `B` импортирует все элементы `A`, _кроме_ `given` экземпляра `tc`. +И наоборот, второй импорт, `import A.given`, импортирует _только_ экземпляр `given`. +Два предложения импорта также могут быть объединены в одно: + +```scala +object B: + import A.{given, *} +``` + +## Обсуждение + +Селектор с подстановочным знаком `*` помещает в область видимости все определения, кроме given-ов или расширений, +тогда как селектор `given` помещает в область видимости _все_ given-ы, включая те, которые являются результатом расширений. + +Эти правила имеют два основных преимущества: + +- понятнее, откуда берутся данные в текущей области видимости. + В частности, невозможно скрыть импортированные given-ы в длинном списке других импортов. +- есть возможность импортировать все given, не импортируя ничего другого. + Это важно, потому что given-ы могут быть анонимными, поэтому обычное использование именованного импорта нецелесообразно. + +Дополнительные примеры синтаксиса "import given" показаны в главе ["Пакеты и импорт"][imports]. + +[imports]: {% link _overviews/scala3-book/packaging-imports.md %} From 7500199d60f2f6687a2fb81c3f2447d68d120f75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9A=D0=BE=D1=80=D1=81?= =?UTF-8?q?=D0=B0=D0=BA=D0=BE=D0=B2?= Date: Thu, 10 Aug 2023 09:45:45 +0300 Subject: [PATCH 3/4] Add ca-given-imports.md in ru --- _ru/scala3/book/ca-given-imports.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/_ru/scala3/book/ca-given-imports.md b/_ru/scala3/book/ca-given-imports.md index 271d622b7e..41439a27be 100644 --- a/_ru/scala3/book/ca-given-imports.md +++ b/_ru/scala3/book/ca-given-imports.md @@ -13,8 +13,8 @@ next-page: ca-type-classes versionSpecific: true --- -Для большей ясности, откуда берутся данные в текущей области видимости, -для импорта экземпляров `given` используется специальная форма оператора `import`. +Для большей ясности, откуда берутся данные в текущей области видимости, +для импорта экземпляров `given` используется специальная форма оператора `import`. Базовая форма показана в этом примере: ```scala @@ -28,8 +28,8 @@ object B: import A.given // импорт экземпляров given ``` -В этом коде предложение `import A.*` объекта `B` импортирует все элементы `A`, _кроме_ `given` экземпляра `tc`. -И наоборот, второй импорт, `import A.given`, импортирует _только_ экземпляр `given`. +В этом коде предложение `import A.*` объекта `B` импортирует все элементы `A`, _кроме_ `given` экземпляра `tc`. +И наоборот, второй импорт, `import A.given`, импортирует _только_ экземпляр `given`. Два предложения импорта также могут быть объединены в одно: ```scala @@ -39,14 +39,14 @@ object B: ## Обсуждение -Селектор с подстановочным знаком `*` помещает в область видимости все определения, кроме given-ов или расширений, +Селектор с подстановочным знаком `*` помещает в область видимости все определения, кроме given-ов или расширений, тогда как селектор `given` помещает в область видимости _все_ given-ы, включая те, которые являются результатом расширений. Эти правила имеют два основных преимущества: -- понятнее, откуда берутся данные в текущей области видимости. +- понятнее, откуда берутся данные в текущей области видимости. В частности, невозможно скрыть импортированные given-ы в длинном списке других импортов. -- есть возможность импортировать все given, не импортируя ничего другого. +- есть возможность импортировать все given, не импортируя ничего другого. Это важно, потому что given-ы могут быть анонимными, поэтому обычное использование именованного импорта нецелесообразно. Дополнительные примеры синтаксиса "import given" показаны в главе ["Пакеты и импорт"][imports]. From 9930545b611d09f327a480fd2e5072cc67f24f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9A=D0=BE=D1=80=D1=81?= =?UTF-8?q?=D0=B0=D0=BA=D0=BE=D0=B2?= Date: Fri, 11 Aug 2023 19:45:13 +0300 Subject: [PATCH 4/4] Add ca-type-classes.md in ru --- _overviews/scala3-book/ca-type-classes.md | 2 +- _ru/scala3/book/ca-type-classes.md | 230 ++++++++++++++++++++++ 2 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 _ru/scala3/book/ca-type-classes.md diff --git a/_overviews/scala3-book/ca-type-classes.md b/_overviews/scala3-book/ca-type-classes.md index 36ae7634cf..71af01513b 100644 --- a/_overviews/scala3-book/ca-type-classes.md +++ b/_overviews/scala3-book/ca-type-classes.md @@ -2,7 +2,7 @@ title: Type Classes type: section description: This page demonstrates how to create and use type classes. -languages: [zh-cn] +languages: [ru, zh-cn] num: 64 previous-page: ca-given-imports next-page: ca-multiversal-equality diff --git a/_ru/scala3/book/ca-type-classes.md b/_ru/scala3/book/ca-type-classes.md new file mode 100644 index 0000000000..95b2d83ecb --- /dev/null +++ b/_ru/scala3/book/ca-type-classes.md @@ -0,0 +1,230 @@ +--- +layout: multipage-overview +title: Классы типов +scala3: true +partof: scala3-book +overview-name: "Scala 3 — Book" +type: section +description: В этой главе демонстрируется создание и использование классов типов. +language: ru +num: 64 +previous-page: ca-given-imports +next-page: +--- + +Класс типов (_type class_) — это абстрактный параметризованный тип, +который позволяет добавлять новое поведение к любому закрытому типу данных без использования подтипов. +Если вы пришли с Java, то можно думать о классах типов как о чем-то вроде [`java.util.Comparator[T]`][comparator]. + +> В статье ["Type Classes as Objects and Implicits"][typeclasses-paper] (2010 г.) обсуждаются основные идеи, +> лежащие в основе классов типов в Scala. +> Несмотря на то, что в статье используется более старая версия Scala, идеи актуальны и по сей день. + +Этот стиль программирования полезен во многих случаях, например: + +- выражение того, как тип, которым вы не владеете, например, из стандартной или сторонней библиотеки, соответствует такому поведению +- добавление поведения к нескольким типам без введения отношений подтипов между этими типами (например, когда один расширяет другой) + +Классы типов — это трейты с одним или несколькими параметрами, +реализации которых предоставляются в виде экземпляров `given` в Scala 3 или `implicit` значений в Scala 2. + +## Пример + +Например, `Show` - хорошо известный класс типов в Haskell, и в следующем коде показан один из способов его реализации в Scala. +Если предположить, что классы Scala не содержат метода `toString`, то можно определить класс типов `Show`, +чтобы добавить это поведение к любому типу, который вы хотите преобразовать в пользовательскую строку. + +### Класс типов + +Первым шагом в создании класса типов является объявление параметризованного trait, содержащего один или несколько абстрактных методов. +Поскольку `Showable` содержит только один метод с именем `show`, он записывается так: + +{% tabs 'definition' class=tabs-scala-version %} +{% tab 'Scala 2' %} + +```scala +// класс типов +trait Showable[A] { + def show(a: A): String +} +``` + +{% endtab %} +{% tab 'Scala 3' %} + +```scala +// класс типов +trait Showable[A]: + extension (a: A) def show: String +``` + +{% endtab %} +{% endtabs %} + +Обратите внимание, что этот подход близок к обычному объектно-ориентированному подходу, +когда обычно trait `Show` определяется следующим образом: + +{% tabs 'trait' class=tabs-scala-version %} +{% tab 'Scala 2' %} + +```scala +// a trait +trait Show { + def show: String +} +``` + +{% endtab %} +{% tab 'Scala 3' %} + +```scala +// a trait +trait Show: + def show: String +``` + +{% endtab %} +{% endtabs %} + +Следует отметить несколько важных моментов: + +1. Классы типов, например, `Showable` принимают параметр типа `A`, чтобы указать, для какого типа мы предоставляем реализацию `show`; + в отличие от классических трейтов, наподобие `Show`. +2. Чтобы добавить функциональность `show` к определенному типу `A`, классический трейт требует наследования `A extends Show`, + в то время как для классов типов нам требуется реализация `Showable[A]`. +3. В Scala 3, чтобы разрешить один и тот же синтаксис вызова метода в обоих случаях `Showable`, + который имитирует синтаксис `Show`, мы определяем `Showable.show` как метод расширения. + +### Реализация конкретных экземпляров + +Следующий шаг — определить, какие классы `Showable` должны работать в вашем приложении, а затем реализовать для них это поведение. +Например, для реализации `Showable` следующего класса `Person`: + +{% tabs 'person' %} +{% tab 'Scala 2 и 3' %} + +```scala +case class Person(firstName: String, lastName: String) +``` + +{% endtab %} +{% endtabs %} + +необходимо определить одно _каноническое значение_ типа `Showable[Person]`, т.е. экземпляр `Showable` для типа `Person`, +как показано в следующем примере кода: + +{% tabs 'instance' class=tabs-scala-version %} +{% tab 'Scala 2' %} + +```scala +implicit val showablePerson: Showable[Person] = new Showable[Person] { + def show(p: Person): String = + s"${p.firstName} ${p.lastName}" +} +``` + +{% endtab %} +{% tab 'Scala 3' %} + +```scala +given Showable[Person] with + extension (p: Person) def show: String = + s"${p.firstName} ${p.lastName}" +``` + +{% endtab %} +{% endtabs %} + +### Использование класса типов + +Теперь вы можете использовать этот класс типов следующим образом: + +{% tabs 'usage' class=tabs-scala-version %} +{% tab 'Scala 2' %} + +```scala +val person = Person("John", "Doe") +println(showablePerson.show(person)) +``` + +Обратите внимание, что на практике классы типов обычно используются со значениями, тип которых неизвестен, +в отличие от type `Person`, как показано в следующем разделе. + +{% endtab %} +{% tab 'Scala 3' %} + +```scala +val person = Person("John", "Doe") +println(person.show) +``` + +{% endtab %} +{% endtabs %} + +Опять же, если бы в Scala не было метода `toString`, доступного для каждого класса, вы могли бы использовать эту технику, +чтобы добавить поведение `Showable` к любому классу, который вы хотите преобразовать в `String`. + +### Написание методов, использующих класс типов + +Как и в случае с наследованием, вы можете определить методы, которые используют `Showable` в качестве параметра типа: + +{% tabs 'method' class=tabs-scala-version %} +{% tab 'Scala 2' %} + +```scala +def showAll[A](as: List[A])(implicit showable: Showable[A]): Unit = + as.foreach(a => println(showable.show(a))) + +showAll(List(Person("Jane"), Person("Mary"))) +``` + +{% endtab %} +{% tab 'Scala 3' %} + +```scala +def showAll[A: Showable](as: List[A]): Unit = + as.foreach(a => println(a.show)) + +showAll(List(Person("Jane"), Person("Mary"))) +``` + +{% endtab %} +{% endtabs %} + +### Класс типов с несколькими методами + +Обратите внимание: если вы хотите создать класс типов с несколькими методами, исходный синтаксис выглядит следующим образом: + +{% tabs 'multiple-methods' class=tabs-scala-version %} +{% tab 'Scala 2' %} + +```scala +trait HasLegs[A] { + def walk(a: A): Unit + def run(a: A): Unit +} +``` + +{% endtab %} +{% tab 'Scala 3' %} + +```scala +trait HasLegs[A]: + extension (a: A) + def walk(): Unit + def run(): Unit +``` + +{% endtab %} +{% endtabs %} + +### Пример из реального мира + +В качестве примера из реального мира, как классы типов используются в Scala 3, +см. обсуждение `CanEqual` в [разделе Multiversal Equality][multiversal]. + +[typeclasses-paper]: https://infoscience.epfl.ch/record/150280/files/TypeClasses.pdf + +[typeclasses-chapter]: {% link _overviews/scala3-book/ca-type-classes.md %} +[comparator]: https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html +[multiversal]: {% link _overviews/scala3-book/ca-multiversal-equality.md %}