Skip to content

Add types-intersection.md and types-union.md in ru #2834

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

Merged
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
2 changes: 1 addition & 1 deletion _overviews/scala3-book/types-intersection.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion _overviews/scala3-book/types-union.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion _ru/scala3/book/types-generics.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ description: В этом разделе представлены парамет
language: ru
num: 49
previous-page: types-inferred
next-page:
next-page: types-intersection
---

Универсальные (_generic_) классы (или trait-ы) принимают тип в качестве _параметра_ в квадратных скобках `[...]`.
Expand Down
76 changes: 76 additions & 0 deletions _ru/scala3/book/types-intersection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
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
---

<span class="tag tag-inline">Только в Scala 3</span>

Используемый для типов оператор `&` создает так называемый _тип пересечения_ (_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`.
111 changes: 111 additions & 0 deletions _ru/scala3/book/types-union.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
---
layout: multipage-overview
title: Объединение типов
scala3: true
partof: scala3-book
overview-name: "Scala 3 — Book"
type: section
description: В этом разделе представлены объединение типов в Scala 3.
language: ru
num: 51
previous-page: types-intersection
next-page:
---

<span class="tag tag-inline">Только в Scala 3</span>

Используемый для типов `|` оператор создает так называемый _тип объединения_ (_union type_).
Тип `А | B` представляет значения, которые относятся **либо** к типу `A`, **либо** к типу `B`.

В следующем примере метод `help` принимает параметр с именем `id` типа объединения `Username | Password`,
который может быть либо `Username`, либо `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)
// дальнейший код ...
```

Мы реализуем метод `help`, разделяя две альтернативы с использованием сопоставления с образцом.

Этот код является гибким и типобезопасным решением.
Если попытаться передать тип, отличный от `Username` или `Password`, компилятор пометит это как ошибку:

```scala
help("hi") // error: Found: ("hi" : String)
// Required: Username | Password
```

Ошибка также будет получена, если попытаться добавить `case` в выражение `match`,
которое не соответствует типам `Username` или `Password`:

```scala
case 1.0 => ??? // Ошибка: это строка не компилируется
```

### Альтернатива объединенным типам

Как показано, объединенные типы могут использоваться для представления вариантов нескольких разных типов,
не требуя, чтобы эти типы были частью специально созданной иерархии классов.

#### Предварительное планирование иерархии классов

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

```scala
trait UsernameOrPassword
case class Username(name: String) extends UsernameOrPassword
case class Password(hash: Hash) extends UsernameOrPassword
def help(id: UsernameOrPassword) = ...
```

Предварительное планирование не очень хорошо масштабируется,
поскольку, например, требования пользователей API могут быть непредсказуемыми.
Кроме того, загромождение иерархии типов маркерами типа `UsernameOrPassword` затрудняет чтение кода.

#### Теговые объединения

Другой альтернативой является задание отдельного типа перечисления, например:

```scala
enum UsernameOrPassword:
case IsUsername(u: Username)
case IsPassword(p: Password)
```

Перечисление `UsernameOrPassword` представляет собой _помеченное_ (_tagged_) объединение `Username` и `Password`.
Однако этот способ моделирования объединения требует _явной упаковки и распаковки_,
и, например, `Username` **не** является подтипом `UsernameOrPassword`.

### Вывод типов объединения

Компилятор присваивает типу объединения выражение, _только если_ такой тип явно задан.
Например, рассмотрим такие значения:

```scala
val name = Username("Eve") // name: Username = Username(Eve)
val password = Password(123) // password: Password = Password(123)
```

В этом 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`.
Если необходим наименьший супертип, его нужно указать явно, как это делается для `b`.

> Типы объединения являются двойственными типам пересечения.
> И как `&` с типами пересечения, `|` также коммутативен: `A | B` того же типа, что и `B | А`.