From 9542f166a42a674964a1117db380e18aed54efb4 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: Wed, 7 Jun 2023 19:22:50 +0300 Subject: [PATCH 1/4] Add types-intersection.md and types-union.md in russian --- _overviews/scala3-book/types-intersection.md | 2 +- _overviews/scala3-book/types-union.md | 2 +- _ru/scala3/book/types-generics.md | 2 +- _ru/scala3/book/types-intersection.md | 72 +++++++++++++++ _ru/scala3/book/types-union.md | 95 ++++++++++++++++++++ 5 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 _ru/scala3/book/types-intersection.md create mode 100644 _ru/scala3/book/types-union.md diff --git a/_overviews/scala3-book/types-intersection.md b/_overviews/scala3-book/types-intersection.md index 8c01dd9b33..66a6f3ec0d 100644 --- a/_overviews/scala3-book/types-intersection.md +++ b/_overviews/scala3-book/types-intersection.md @@ -2,7 +2,7 @@ title: Intersection Types type: section description: This section introduces and demonstrates intersection types in Scala 3. -languages: [zh-cn] +languages: [ru, zh-cn] num: 50 previous-page: types-generics next-page: types-union diff --git a/_overviews/scala3-book/types-union.md b/_overviews/scala3-book/types-union.md index 271c9a0148..50c5919980 100644 --- a/_overviews/scala3-book/types-union.md +++ b/_overviews/scala3-book/types-union.md @@ -2,7 +2,7 @@ title: Union Types type: section description: This section introduces and demonstrates union types in Scala 3. -languages: [zh-cn] +languages: [ru, zh-cn] num: 51 previous-page: types-intersection next-page: types-adts-gadts diff --git a/_ru/scala3/book/types-generics.md b/_ru/scala3/book/types-generics.md index 799a85e3d5..653d6cacbd 100644 --- a/_ru/scala3/book/types-generics.md +++ b/_ru/scala3/book/types-generics.md @@ -9,7 +9,7 @@ description: В этом разделе представлены парамет language: ru num: 49 previous-page: types-inferred -next-page: +next-page: types-intersection --- Универсальные (_generic_) классы (или trait-ы) принимают тип в качестве _параметра_ в квадратных скобках `[...]`. diff --git a/_ru/scala3/book/types-intersection.md b/_ru/scala3/book/types-intersection.md new file mode 100644 index 0000000000..4d5082ff98 --- /dev/null +++ b/_ru/scala3/book/types-intersection.md @@ -0,0 +1,72 @@ +--- +layout: multipage-overview +title: Пересечение типов +scala3: true +partof: scala3-book +overview-name: "Scala 3 — Book" +type: section +description: В этом разделе представлены пересечение типов в Scala 3. +language: ru +num: 50 +previous-page: types-generics +next-page: types-union +--- + +Только в Scala 3 + +Используемый для типов оператор `&` создает так называемый _тип пересечения_ (_intersection type_). +Тип `A & B` представляет собой значения, которые **одновременно** относятся как к типу `A`, так и к типу `B`. +Например, в следующем примере используется тип пересечения `Resettable & Growable[String]`: + +{% tabs intersection-reset-grow %} + +{% tab 'Только в Scala 3' %} + +```scala +trait Resettable: + def reset(): Unit + +trait Growable[A]: + def add(a: A): Unit + +def f(x: Resettable & Growable[String]): Unit = + x.reset() + x.add("first") +``` + +{% endtab %} + +{% endtabs %} + +В методе `f` в этом примере параметр `x` должен быть _одновременно_ как `Resettable`, так и `Growable[String]`. + +Все _члены_ типа пересечения `A & B` являются типом `A` и типом `B`. +Следовательно, как показано, для `Resettable & Growable[String]` доступны методы `reset` и `add`. + +Пересечение типов может быть полезно для _структурного_ описания требований. +В примере выше для `f` мы прямо заявляем, что нас устраивает любое значение для `x`, +если оно является подтипом как `Resettable`, так и `Growable`. +**Нет** необходимости создавать _номинальный_ вспомогательный trait, подобный следующему: + +{% tabs normal-trait class=tabs-scala-version %} +{% tab 'Scala 2' %} +```scala +trait Both[A] extends Resettable with Growable[A] +def f(x: Both[String]): Unit +``` +{% endtab %} + +{% tab 'Scala 3' %} +```scala +trait Both[A] extends Resettable, Growable[A] +def f(x: Both[String]): Unit +``` +{% endtab %} +{% endtabs %} + +Существует важное различие между двумя вариантами определения `f`: +в то время как оба позволяют вызывать `f` с экземплярами `Both`, +только первый позволяет передавать экземпляры, +которые являются подтипами `Resettable` и `Growable[String]`, _но не_ `Both[String]`. + +> Обратите внимание, что `&` _коммутативно_: `A & B` имеет тот же тип, что и `B & A`. diff --git a/_ru/scala3/book/types-union.md b/_ru/scala3/book/types-union.md new file mode 100644 index 0000000000..271c9a0148 --- /dev/null +++ b/_ru/scala3/book/types-union.md @@ -0,0 +1,95 @@ +--- +title: Union Types +type: section +description: This section introduces and demonstrates union types in Scala 3. +languages: [zh-cn] +num: 51 +previous-page: types-intersection +next-page: types-adts-gadts +scala3: true +versionSpecific: true +--- + +Used on types, the `|` operator creates a so-called _union type_. +The type `A | B` represents values that are **either** of the type `A` **or** of the type `B`. + +In the following example, the `help` method accepts a parameter named `id` of the union type `Username | Password`, that can be either a `Username` or a `Password`: + +```scala +case class Username(name: String) +case class Password(hash: Hash) + +def help(id: Username | Password) = + val user = id match + case Username(name) => lookupName(name) + case Password(hash) => lookupPassword(hash) + // more code here ... +``` +We implement the method `help` by distinguishing between the two alternatives using pattern matching. + +This code is a flexible and type-safe solution. +If you attempt to pass in a type other than a `Username` or `Password`, the compiler flags it as an error: + +```scala +help("hi") // error: Found: ("hi" : String) + // Required: Username | Password +``` + +You’ll also get an error if you attempt to add a `case` to the `match` expression that doesn’t match the `Username` or `Password` types: + +```scala +case 1.0 => ??? // ERROR: this line won’t compile +``` + +### Alternative to Union Types +As shown, union types can be used to represent alternatives of several different types, without requiring those types to be part of a custom-crafted class hierarchy, or requiring explicit wrapping. + +#### Pre-planning the Class Hierarchy +Without union types, it would require pre-planning of the class hierarchy, like the following example illustrates: + +```scala +trait UsernameOrPassword +case class Username(name: String) extends UsernameOrPassword +case class Password(hash: Hash) extends UsernameOrPassword +def help(id: UsernameOrPassword) = ... +``` + +Pre-planning does not scale very well since, for example, requirements of API users might not be foreseeable. +Additionally, cluttering the type hierarchy with marker traits like `UsernameOrPassword` also makes the code more difficult to read. + +#### Tagged Unions +Another alternative is to define a separate enumeration type like: + +```scala +enum UsernameOrPassword: + case IsUsername(u: Username) + case IsPassword(p: Password) +``` +The enumeration `UsernameOrPassword` represents a _tagged_ union of `Username` and `Password`. +However, this way of modeling the union requires _explicit wrapping and unwrapping_ and, for instance, `Username` is **not** a subtype of `UsernameOrPassword`. + +### Inference of Union Types +The compiler assigns a union type to an expression _only if_ such a type is explicitly given. +For instance, given these values: + +```scala +val name = Username("Eve") // name: Username = Username(Eve) +val password = Password(123) // password: Password = Password(123) +``` + +This REPL example shows how a union type can be used when binding a variable to the result of an `if`/`else` expression: + +```` +scala> val a = if true then name else password +val a: Object = Username(Eve) + +scala> val b: Password | Username = if true then name else password +val b: Password | Username = Username(Eve) +```` + +The type of `a` is `Object`, which is a supertype of `Username` and `Password`, but not the *least* supertype, `Password | Username`. +If you want the least supertype you have to give it explicitly, as is done for `b`. + +> Union types are duals of intersection types. +> And like `&` with intersection types, `|` is also commutative: `A | B` is the same type as `B | A`. + From 1768f5196a5e448bfc9248ab65fbdd3006d5f24d 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: Wed, 7 Jun 2023 19:38:17 +0300 Subject: [PATCH 2/4] Add types-intersection.md and types-union.md in russian --- _ru/scala3/book/types-intersection.md | 22 ++++--- _ru/scala3/book/types-union.md | 84 ++++++++++++++++----------- 2 files changed, 63 insertions(+), 43 deletions(-) diff --git a/_ru/scala3/book/types-intersection.md b/_ru/scala3/book/types-intersection.md index 4d5082ff98..6fa78e33ab 100644 --- a/_ru/scala3/book/types-intersection.md +++ b/_ru/scala3/book/types-intersection.md @@ -14,8 +14,8 @@ next-page: types-union Только в Scala 3 -Используемый для типов оператор `&` создает так называемый _тип пересечения_ (_intersection type_). -Тип `A & B` представляет собой значения, которые **одновременно** относятся как к типу `A`, так и к типу `B`. +Используемый для типов оператор `&` создает так называемый _тип пересечения_ (_intersection type_). +Тип `A & B` представляет собой значения, которые **одновременно** относятся как к типу `A`, так и к типу `B`. Например, в следующем примере используется тип пересечения `Resettable & Growable[String]`: {% tabs intersection-reset-grow %} @@ -40,33 +40,37 @@ def f(x: Resettable & Growable[String]): Unit = В методе `f` в этом примере параметр `x` должен быть _одновременно_ как `Resettable`, так и `Growable[String]`. -Все _члены_ типа пересечения `A & B` являются типом `A` и типом `B`. +Все _члены_ типа пересечения `A & B` являются типом `A` и типом `B`. Следовательно, как показано, для `Resettable & Growable[String]` доступны методы `reset` и `add`. -Пересечение типов может быть полезно для _структурного_ описания требований. -В примере выше для `f` мы прямо заявляем, что нас устраивает любое значение для `x`, -если оно является подтипом как `Resettable`, так и `Growable`. +Пересечение типов может быть полезно для _структурного_ описания требований. +В примере выше для `f` мы прямо заявляем, что нас устраивает любое значение для `x`, +если оно является подтипом как `Resettable`, так и `Growable`. **Нет** необходимости создавать _номинальный_ вспомогательный trait, подобный следующему: {% tabs normal-trait class=tabs-scala-version %} {% tab 'Scala 2' %} + ```scala trait Both[A] extends Resettable with Growable[A] def f(x: Both[String]): Unit ``` + {% endtab %} {% tab 'Scala 3' %} + ```scala trait Both[A] extends Resettable, Growable[A] def f(x: Both[String]): Unit ``` + {% endtab %} {% endtabs %} -Существует важное различие между двумя вариантами определения `f`: -в то время как оба позволяют вызывать `f` с экземплярами `Both`, -только первый позволяет передавать экземпляры, +Существует важное различие между двумя вариантами определения `f`: +в то время как оба позволяют вызывать `f` с экземплярами `Both`, +только первый позволяет передавать экземпляры, которые являются подтипами `Resettable` и `Growable[String]`, _но не_ `Both[String]`. > Обратите внимание, что `&` _коммутативно_: `A & B` имеет тот же тип, что и `B & A`. diff --git a/_ru/scala3/book/types-union.md b/_ru/scala3/book/types-union.md index 271c9a0148..99fdd14275 100644 --- a/_ru/scala3/book/types-union.md +++ b/_ru/scala3/book/types-union.md @@ -1,19 +1,24 @@ --- -title: Union Types +layout: multipage-overview +title: Объединение типов +scala3: true +partof: scala3-book +overview-name: "Scala 3 — Book" type: section -description: This section introduces and demonstrates union types in Scala 3. -languages: [zh-cn] +description: В этом разделе представлены объединение типов в Scala 3. +language: ru num: 51 previous-page: types-intersection -next-page: types-adts-gadts -scala3: true -versionSpecific: true +next-page: --- -Used on types, the `|` operator creates a so-called _union type_. -The type `A | B` represents values that are **either** of the type `A` **or** of the type `B`. +Только в Scala 3 + +Используемый для типов `|` оператор создает так называемый _тип объединения_ (_union type_). +Тип `А | B` представляет значения, которые относятся **либо** к типу `A`, **либо** к типу `B`. -In the following example, the `help` method accepts a parameter named `id` of the union type `Username | Password`, that can be either a `Username` or a `Password`: +В следующем примере метод `help` принимает параметр с именем `id` типа объединения `Username | Password`, +который может быть либо `Username`, либо `Password`: ```scala case class Username(name: String) @@ -23,29 +28,34 @@ def help(id: Username | Password) = val user = id match case Username(name) => lookupName(name) case Password(hash) => lookupPassword(hash) - // more code here ... + // ещё больше кода здесь ... ``` -We implement the method `help` by distinguishing between the two alternatives using pattern matching. -This code is a flexible and type-safe solution. -If you attempt to pass in a type other than a `Username` or `Password`, the compiler flags it as an error: +Мы реализуем метод `help`, разделяя две альтернативы, используя сопоставление с образцом. + +Этот код является гибким и типобезопасным решением. +Если попытаться передать тип, отличный от `Username` или `Password`, компилятор пометит это как ошибку: ```scala help("hi") // error: Found: ("hi" : String) // Required: Username | Password ``` -You’ll also get an error if you attempt to add a `case` to the `match` expression that doesn’t match the `Username` or `Password` types: +Ошибка также будет получена, если попытаться добавить `case` в выражение `match`, +которое не соответствует типам `Username` или `Password`: ```scala -case 1.0 => ??? // ERROR: this line won’t compile +case 1.0 => ??? // Ошибка: это строка не компилируется ``` -### Alternative to Union Types -As shown, union types can be used to represent alternatives of several different types, without requiring those types to be part of a custom-crafted class hierarchy, or requiring explicit wrapping. +### Альтернатива объединенным типам + +Как показано, объединенные типы могут использоваться для представления вариантов нескольких разных типов, +не требуя, чтобы эти типы были частью специально созданной иерархии классов. -#### Pre-planning the Class Hierarchy -Without union types, it would require pre-planning of the class hierarchy, like the following example illustrates: +#### Предварительное планирование иерархии классов + +Другие языки требуют предварительного планирования иерархии классов, как показано в следующем примере: ```scala trait UsernameOrPassword @@ -54,30 +64,36 @@ case class Password(hash: Hash) extends UsernameOrPassword def help(id: UsernameOrPassword) = ... ``` -Pre-planning does not scale very well since, for example, requirements of API users might not be foreseeable. -Additionally, cluttering the type hierarchy with marker traits like `UsernameOrPassword` also makes the code more difficult to read. +Предварительное планирование не очень хорошо масштабируется, +поскольку, например, требования пользователей API могут быть непредсказуемыми. +Кроме того, загромождение иерархии типов маркерами типа `UsernameOrPassword` затрудняет чтение кода. + +#### Теговые объединения -#### Tagged Unions -Another alternative is to define a separate enumeration type like: +Другой альтернативой является задание отдельного типа перечисления, например: ```scala enum UsernameOrPassword: case IsUsername(u: Username) case IsPassword(p: Password) ``` -The enumeration `UsernameOrPassword` represents a _tagged_ union of `Username` and `Password`. -However, this way of modeling the union requires _explicit wrapping and unwrapping_ and, for instance, `Username` is **not** a subtype of `UsernameOrPassword`. -### Inference of Union Types -The compiler assigns a union type to an expression _only if_ such a type is explicitly given. -For instance, given these values: +Перечисление `UsernameOrPassword` представляет собой _помеченное_ (_tagged_) объединение `Username` и `Password`. +Однако этот способ моделирования объединения требует _явной упаковки и распаковки_, +и, например, `Username` **не** является подтипом `UsernameOrPassword`. + +### Вывод типов объединения + +Компилятор присваивает типу объединения выражение, _только если_ такой тип явно задан. +Например, рассмотрим такие значения: ```scala val name = Username("Eve") // name: Username = Username(Eve) val password = Password(123) // password: Password = Password(123) ``` -This REPL example shows how a union type can be used when binding a variable to the result of an `if`/`else` expression: +В этом REPL примере показано, +как можно использовать тип объединения при привязке переменной к результату выражения `if`/`else`: ```` scala> val a = if true then name else password @@ -87,9 +103,9 @@ scala> val b: Password | Username = if true then name else password val b: Password | Username = Username(Eve) ```` -The type of `a` is `Object`, which is a supertype of `Username` and `Password`, but not the *least* supertype, `Password | Username`. -If you want the least supertype you have to give it explicitly, as is done for `b`. - -> Union types are duals of intersection types. -> And like `&` with intersection types, `|` is also commutative: `A | B` is the same type as `B | A`. +Типом `a` является `Object`, который является супертипом `Username` и `Password`, +но не _наименьшим_ супертипом, `Password | Username`. +Если необходим наименьший супертип, его нужно указать явно, как это делается для `b`. +> Типы объединения являются двойственными типам пересечения. +> И как `&` с типами пересечения, `|` также коммутативен: `A | B` того же типа, что и `B | А`. From 68910c0927b237a625d2b412fd5ed2954d14552a 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: Wed, 7 Jun 2023 19:42:05 +0300 Subject: [PATCH 3/4] Add types-intersection.md and types-union.md in russian --- _ru/scala3/book/types-union.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/_ru/scala3/book/types-union.md b/_ru/scala3/book/types-union.md index 99fdd14275..7c6e5d9d61 100644 --- a/_ru/scala3/book/types-union.md +++ b/_ru/scala3/book/types-union.md @@ -14,10 +14,10 @@ next-page: Только в Scala 3 -Используемый для типов `|` оператор создает так называемый _тип объединения_ (_union type_). +Используемый для типов `|` оператор создает так называемый _тип объединения_ (_union type_). Тип `А | B` представляет значения, которые относятся **либо** к типу `A`, **либо** к типу `B`. -В следующем примере метод `help` принимает параметр с именем `id` типа объединения `Username | Password`, +В следующем примере метод `help` принимает параметр с именем `id` типа объединения `Username | Password`, который может быть либо `Username`, либо `Password`: ```scala @@ -33,7 +33,7 @@ def help(id: Username | Password) = Мы реализуем метод `help`, разделяя две альтернативы, используя сопоставление с образцом. -Этот код является гибким и типобезопасным решением. +Этот код является гибким и типобезопасным решением. Если попытаться передать тип, отличный от `Username` или `Password`, компилятор пометит это как ошибку: ```scala @@ -41,7 +41,7 @@ help("hi") // error: Found: ("hi" : String) // Required: Username | Password ``` -Ошибка также будет получена, если попытаться добавить `case` в выражение `match`, +Ошибка также будет получена, если попытаться добавить `case` в выражение `match`, которое не соответствует типам `Username` или `Password`: ```scala @@ -50,7 +50,7 @@ case 1.0 => ??? // Ошибка: это строка не компилируе ### Альтернатива объединенным типам -Как показано, объединенные типы могут использоваться для представления вариантов нескольких разных типов, +Как показано, объединенные типы могут использоваться для представления вариантов нескольких разных типов, не требуя, чтобы эти типы были частью специально созданной иерархии классов. #### Предварительное планирование иерархии классов @@ -64,8 +64,8 @@ case class Password(hash: Hash) extends UsernameOrPassword def help(id: UsernameOrPassword) = ... ``` -Предварительное планирование не очень хорошо масштабируется, -поскольку, например, требования пользователей API могут быть непредсказуемыми. +Предварительное планирование не очень хорошо масштабируется, +поскольку, например, требования пользователей API могут быть непредсказуемыми. Кроме того, загромождение иерархии типов маркерами типа `UsernameOrPassword` затрудняет чтение кода. #### Теговые объединения @@ -78,13 +78,13 @@ enum UsernameOrPassword: case IsPassword(p: Password) ``` -Перечисление `UsernameOrPassword` представляет собой _помеченное_ (_tagged_) объединение `Username` и `Password`. -Однако этот способ моделирования объединения требует _явной упаковки и распаковки_, +Перечисление `UsernameOrPassword` представляет собой _помеченное_ (_tagged_) объединение `Username` и `Password`. +Однако этот способ моделирования объединения требует _явной упаковки и распаковки_, и, например, `Username` **не** является подтипом `UsernameOrPassword`. ### Вывод типов объединения -Компилятор присваивает типу объединения выражение, _только если_ такой тип явно задан. +Компилятор присваивает типу объединения выражение, _только если_ такой тип явно задан. Например, рассмотрим такие значения: ```scala @@ -92,20 +92,20 @@ val name = Username("Eve") // name: Username = Username(Eve) val password = Password(123) // password: Password = Password(123) ``` -В этом REPL примере показано, +В этом REPL примере показано, как можно использовать тип объединения при привязке переменной к результату выражения `if`/`else`: -```` +``` scala> val a = if true then name else password val a: Object = Username(Eve) scala> val b: Password | Username = if true then name else password val b: Password | Username = Username(Eve) -```` +``` -Типом `a` является `Object`, который является супертипом `Username` и `Password`, -но не _наименьшим_ супертипом, `Password | Username`. +Типом `a` является `Object`, который является супертипом `Username` и `Password`, +но не _наименьшим_ супертипом, `Password | Username`. Если необходим наименьший супертип, его нужно указать явно, как это делается для `b`. -> Типы объединения являются двойственными типам пересечения. +> Типы объединения являются двойственными типам пересечения. > И как `&` с типами пересечения, `|` также коммутативен: `A | B` того же типа, что и `B | А`. From 4caf1bfdb9f8bf2ff561639f2dbeb1479f37d651 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: Wed, 7 Jun 2023 19:49:15 +0300 Subject: [PATCH 4/4] Add types-intersection.md and types-union.md in russian --- _ru/scala3/book/types-union.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_ru/scala3/book/types-union.md b/_ru/scala3/book/types-union.md index 7c6e5d9d61..35a0f440f6 100644 --- a/_ru/scala3/book/types-union.md +++ b/_ru/scala3/book/types-union.md @@ -28,10 +28,10 @@ def help(id: Username | Password) = val user = id match case Username(name) => lookupName(name) case Password(hash) => lookupPassword(hash) - // ещё больше кода здесь ... + // дальнейший код ... ``` -Мы реализуем метод `help`, разделяя две альтернативы, используя сопоставление с образцом. +Мы реализуем метод `help`, разделяя две альтернативы с использованием сопоставления с образцом. Этот код является гибким и типобезопасным решением. Если попытаться передать тип, отличный от `Username` или `Password`, компилятор пометит это как ошибку: