Skip to content

Commit 1ebe208

Browse files
artemkorsakovjulienrf
authored andcommitted
Add types pages in russian
1 parent 501535e commit 1ebe208

9 files changed

+498
-5
lines changed

_overviews/scala3-book/types-dependent-function.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Dependent Function Types
33
type: section
44
description: This section introduces and demonstrates dependent function types in Scala 3.
5-
languages: [zh-cn]
5+
languages: [ru, zh-cn]
66
num: 57
77
previous-page: types-structural
88
next-page: types-others

_overviews/scala3-book/types-opaque-types.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Opaque Types
33
type: section
44
description: This section introduces and demonstrates opaque types in Scala 3.
5-
languages: [zh-cn]
5+
languages: [ru, zh-cn]
66
num: 55
77
previous-page: types-variance
88
next-page: types-structural

_overviews/scala3-book/types-others.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Other Types
33
type: section
44
description: This section mentions other advanced types in Scala 3.
5-
languages: [zh-cn]
5+
languages: [ru, zh-cn]
66
num: 58
77
previous-page: types-dependent-function
88
next-page: ca-contextual-abstractions-intro

_overviews/scala3-book/types-structural.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Structural Types
33
type: section
44
description: This section introduces and demonstrates structural types in Scala 3.
5-
languages: [zh-cn]
5+
languages: [ru, zh-cn]
66
num: 56
77
previous-page: types-opaque-types
88
next-page: types-dependent-function
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
---
2+
layout: multipage-overview
3+
title: Зависимые типы функций
4+
scala3: true
5+
partof: scala3-book
6+
overview-name: "Scala 3 — Book"
7+
type: section
8+
description: В этом разделе представлены и демонстрируются зависимые типы функций в Scala 3.
9+
language: ru
10+
num: 57
11+
previous-page: types-structural
12+
next-page: types-others
13+
---
14+
15+
_Зависимый тип функции_ (_dependent function type_) описывает типы функций,
16+
где тип результата может зависеть от значений параметров функции.
17+
Концепция зависимых типов и типов зависимых функций является более продвинутой,
18+
и обычно с ней сталкиваются только при разработке собственных библиотек или использовании расширенных библиотек.
19+
20+
## Зависимые типы методов
21+
22+
Рассмотрим следующий пример гетерогенной базы данных, в которой могут храниться значения разных типов.
23+
Ключ содержит информацию о типе соответствующего значения:
24+
25+
```scala
26+
trait Key { type Value }
27+
28+
trait DB {
29+
def get(k: Key): Option[k.Value] // зависимый метод
30+
}
31+
```
32+
33+
Получив ключ, метод `get` предоставляет доступ к карте и потенциально возвращает сохраненное значение типа `k.Value`.
34+
Мы можем прочитать этот _path-dependent type_ как:
35+
"в зависимости от конкретного типа аргумента `k` возвращается соответствующее значение".
36+
37+
Например, у нас могут быть следующие ключи:
38+
39+
```scala
40+
object Name extends Key { type Value = String }
41+
object Age extends Key { type Value = Int }
42+
```
43+
44+
Вызовы метода `get` теперь будут возвращать такие типы:
45+
46+
```scala
47+
val db: DB = ...
48+
val res1: Option[String] = db.get(Name)
49+
val res2: Option[Int] = db.get(Age)
50+
```
51+
52+
Вызов метода `db.get(Name)` возвращает значение типа `Option[String]`,
53+
а вызов `db.get(Age)` возвращает значение типа `Option[Int]`.
54+
Тип возвращаемого значения _зависит_ от конкретного типа аргумента, переданного для `get` — отсюда и название _dependent type_.
55+
56+
## Зависимые типы функций
57+
58+
Как видно выше, в Scala 2 уже была поддержка зависимых типов методов.
59+
Однако создание значений типа `DB` довольно громоздко:
60+
61+
```scala
62+
// создание пользователя DB
63+
def user(db: DB): Unit =
64+
db.get(Name) ... db.get(Age)
65+
66+
// создание экземпляра DB и передача его `user`
67+
user(new DB {
68+
def get(k: Key): Option[k.Value] = ... // реализация DB
69+
})
70+
```
71+
72+
Необходимо вручную создать анонимный внутренний класс `DB`, реализующий метод `get`.
73+
Для кода, основанного на создании множества различных экземпляров `DB`, это очень утомительно.
74+
75+
Трейт `DB` имеет только один абстрактный метод `get`.
76+
Было бы неплохо использовать в этом месте лямбда-синтаксис?
77+
78+
```scala
79+
user { k =>
80+
... // реализация DB
81+
}
82+
```
83+
84+
На самом деле, в Scala 3 теперь это возможно! Можно определить `DB` как _зависимый тип функции_:
85+
86+
```scala
87+
type DB = (k: Key) => Option[k.Value]
88+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
89+
// зависимый тип функции
90+
```
91+
92+
Учитывая это определение `DB`, можно использовать приведенный выше вызов `user`.
93+
94+
Подробнее о внутреннем устройстве зависимых типов функций можно прочитать в [справочной документации][ref].
95+
96+
## Практический пример: числовые выражения
97+
98+
Предположим, что необходимо определить модуль, который абстрагируется от внутреннего представления чисел.
99+
Это может быть полезно, например, для реализации библиотек для автоматического дифференцирования.
100+
101+
Начнем с определения модуля для чисел:
102+
103+
```scala
104+
trait Nums:
105+
// тип Num оставлен абстрактным
106+
type Num
107+
108+
// некоторые операции над числами
109+
def lit(d: Double): Num
110+
def add(l: Num, r: Num): Num
111+
def mul(l: Num, r: Num): Num
112+
```
113+
114+
> Здесь опускается конкретная реализация `Nums`, но в качестве упражнения можно реализовать `Nums`,
115+
> назначив тип `Num = Double` и реализуя соответствующие методы.
116+
117+
Программа, использующая числовую абстракцию, теперь имеет следующий тип:
118+
119+
```scala
120+
type Prog = (n: Nums) => n.Num => n.Num
121+
122+
val ex: Prog = nums => x => nums.add(nums.lit(0.8), x)
123+
```
124+
125+
Тип функции, которая вычисляет производную, наподобие `ex`:
126+
127+
```scala
128+
def derivative(input: Prog): Double
129+
```
130+
131+
Учитывая удобство зависимых типов функций, вызов этой функции в разных программах прост:
132+
133+
```scala
134+
derivative { nums => x => x }
135+
derivative { nums => x => nums.add(nums.lit(0.8), x) }
136+
// ...
137+
```
138+
139+
Напомним, что та же программа в приведенной выше кодировке будет выглядеть так:
140+
141+
```scala
142+
derivative(new Prog {
143+
def apply(nums: Nums)(x: nums.Num): nums.Num = x
144+
})
145+
derivative(new Prog {
146+
def apply(nums: Nums)(x: nums.Num): nums.Num = nums.add(nums.lit(0.8), x)
147+
})
148+
// ...
149+
```
150+
151+
#### Комбинация с контекстными функциями
152+
153+
Комбинация методов расширения, [контекстных функций][ctx-fun] и зависимых функций обеспечивает мощный инструмент для разработчиков библиотек.
154+
Например, мы можем уточнить нашу библиотеку, как указано выше, следующим образом:
155+
156+
```scala
157+
trait NumsDSL extends Nums:
158+
extension (x: Num)
159+
def +(y: Num) = add(x, y)
160+
def *(y: Num) = mul(x, y)
161+
162+
def const(d: Double)(using n: Nums): n.Num = n.lit(d)
163+
164+
type Prog = (n: NumsDSL) ?=> n.Num => n.Num
165+
// ^^^
166+
// prog теперь - контекстная функция,
167+
// которая неявно предполагает NumsDSL в контексте вызова
168+
169+
def derivative(input: Prog): Double = ...
170+
171+
// теперь нам не нужно упоминать Nums в приведенных ниже примерах
172+
derivative { x => const(1.0) + x }
173+
derivative { x => x * x + const(2.0) }
174+
// ...
175+
```
176+
177+
[ref]: {{ site.scala3ref }}/new-types/dependent-function-types.html
178+
[ctx-fun]: {{ site.scala3ref }}/contextual/context-functions.html

_ru/scala3/book/types-opaque-types.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
---
2+
layout: multipage-overview
3+
title: Непрозрачные типы
4+
scala3: true
5+
partof: scala3-book
6+
overview-name: "Scala 3 — Book"
7+
type: section
8+
description: В этом разделе представлены и демонстрируются непрозрачные типы в Scala 3.
9+
language: ru
10+
num: 55
11+
previous-page: types-variance
12+
next-page: types-structural
13+
---
14+
15+
В Scala 3 _непрозрачные псевдонимы типов_ (_opaque type aliases_) обеспечивают абстракции типов без каких-либо **накладных расходов**.
16+
17+
## Накладные расходы на абстракцию
18+
19+
Предположим, что необходимо определить модуль,
20+
предлагающий арифметические операции над числами, которые представлены их логарифмами.
21+
Это может быть полезно для повышения точности, когда числовые значения очень большие или близкие к нулю.
22+
23+
Поскольку важно отличать "обычные" `Double` от чисел, хранящихся в виде их логарифмов, введем класс `Logarithm`:
24+
25+
```scala
26+
class Logarithm(protected val underlying: Double):
27+
def toDouble: Double = math.exp(underlying)
28+
def + (that: Logarithm): Logarithm =
29+
// здесь используется метод apply сопутствующего объекта
30+
Logarithm(this.toDouble + that.toDouble)
31+
def * (that: Logarithm): Logarithm =
32+
new Logarithm(this.underlying + that.underlying)
33+
34+
object Logarithm:
35+
def apply(d: Double): Logarithm = new Logarithm(math.log(d))
36+
```
37+
38+
Метод `apply` сопутствующего объекта позволяет создавать значения типа `Logarithm`,
39+
которые можно использовать следующим образом:
40+
41+
```scala
42+
val l2 = Logarithm(2.0)
43+
val l3 = Logarithm(3.0)
44+
println((l2 * l3).toDouble) // выводит 6.0
45+
println((l2 + l3).toDouble) // выводит 4.999...
46+
```
47+
48+
В то время как класс `Logarithm` предлагает хорошую абстракцию для значений `Double`,
49+
которые хранятся в этой конкретной логарифмической форме,
50+
это накладывает серьезные накладные расходы на производительность:
51+
для каждой отдельной математической операции нужно извлекать значение `underlying`,
52+
а затем снова обернуть его в новый экземпляр `Logarithm`.
53+
54+
## Модульные абстракции
55+
56+
Рассмотрим другой подход к реализации той же библиотеки.
57+
На этот раз вместо того, чтобы определять `Logarithm` как класс, определяем его с помощью _псевдонима типа_.
58+
Во-первых, зададим абстрактный интерфейс модуля:
59+
60+
```scala
61+
trait Logarithms:
62+
63+
type Logarithm
64+
65+
// операции на Logarithm
66+
def add(x: Logarithm, y: Logarithm): Logarithm
67+
def mul(x: Logarithm, y: Logarithm): Logarithm
68+
69+
// функции конвертации между Double и Logarithm
70+
def make(d: Double): Logarithm
71+
def extract(x: Logarithm): Double
72+
73+
// методы расширения, для вызова `add` и `mul` в качестве "методов" на Logarithm
74+
extension (x: Logarithm)
75+
def toDouble: Double = extract(x)
76+
def + (y: Logarithm): Logarithm = add(x, y)
77+
def * (y: Logarithm): Logarithm = mul(x, y)
78+
```
79+
80+
Теперь давайте реализуем этот абстрактный интерфейс, задав тип `Logarithm` равным `Double`:
81+
82+
```scala
83+
object LogarithmsImpl extends Logarithms:
84+
85+
type Logarithm = Double
86+
87+
// операции на Logarithm
88+
def add(x: Logarithm, y: Logarithm): Logarithm = make(x.toDouble + y.toDouble)
89+
def mul(x: Logarithm, y: Logarithm): Logarithm = x + y
90+
91+
// функции конвертации между Double и Logarithm
92+
def make(d: Double): Logarithm = math.log(d)
93+
def extract(x: Logarithm): Double = math.exp(x)
94+
```
95+
96+
В рамках реализации `LogarithmsImpl` уравнение `Logarithm = Double` позволяет реализовать различные методы.
97+
98+
#### Дырявые абстракции
99+
100+
Однако эта абстракция немного "дырява".
101+
Мы должны убедиться, что всегда программируем _только_ с абстрактным интерфейсом `Logarithms`
102+
и никогда не используем `LogarithmsImpl` напрямую.
103+
Прямое использование `LogarithmsImpl` сделало бы равенство `Logarithm = Double` видимым для пользователя,
104+
который может случайно использовать `Double` там, где ожидается логарифмическое удвоение.
105+
Например:
106+
107+
```scala
108+
import LogarithmsImpl.*
109+
val l: Logarithm = make(1.0)
110+
val d: Double = l // проверка типов ДОЗВОЛЯЕТ равенство!
111+
```
112+
113+
Необходимость разделения модуля на абстрактный интерфейс и реализацию может быть полезной,
114+
но также требует больших усилий, чтобы просто скрыть детали реализации `Logarithm`.
115+
Программирование с использованием абстрактного модуля `Logarithms` может быть очень утомительным
116+
и часто требует использования дополнительных функций, таких как типы, зависящие от пути, как в следующем примере:
117+
118+
```scala
119+
def someComputation(L: Logarithms)(init: L.Logarithm): L.Logarithm = ...
120+
```
121+
122+
#### Накладные расходы упаковки/распаковки
123+
124+
Абстракции типов, такие как `type Logarithm`, [стираются](https://www.scala-lang.org/files/archive/spec/2.13/03-types.html#type-erasure)
125+
в соответствии с их привязкой (`Any` - в нашем случае).
126+
То есть, хотя нам не нужно вручную переносить и разворачивать значение `Double`,
127+
все равно будут некоторые накладные расходы, связанные с упаковкой примитивного типа `Double`.
128+
129+
## Непрозрачные типы
130+
131+
Вместо того чтобы вручную разбивать компонент `Logarithms` на абстрактную часть и на конкретную реализацию,
132+
можно просто использовать opaque типы для достижения аналогичного эффекта:
133+
134+
```scala
135+
object Logarithms:
136+
//vvvvvv это важное различие!
137+
opaque type Logarithm = Double
138+
139+
object Logarithm:
140+
def apply(d: Double): Logarithm = math.log(d)
141+
142+
extension (x: Logarithm)
143+
def toDouble: Double = math.exp(x)
144+
def + (y: Logarithm): Logarithm = Logarithm(math.exp(x) + math.exp(y))
145+
def * (y: Logarithm): Logarithm = x + y
146+
```
147+
148+
Тот факт, что `Logarithm` совпадает с `Double`, известен только в области, где он определен,
149+
которая в приведенном выше примере соответствует объекту `Logarithms`.
150+
Равенство `Logarithm = Double` может использоваться для реализации методов (например, `*` и `toDouble`).
151+
152+
Однако вне модуля тип `Logarithm` полностью инкапсулирован или «непрозрачен».
153+
Для пользователей `Logarithm`-а невозможно обнаружить, что `Logarithm` на самом деле реализован как `Double`:
154+
155+
```scala
156+
import Logarithms.*
157+
val log2 = Logarithm(2.0)
158+
val log3 = Logarithm(3.0)
159+
println((log2 * log3).toDouble) // выводит 6.0
160+
println((log2 + log3).toDouble) // выводит 4.999...
161+
162+
val d: Double = log2 // ERROR: Found Logarithm required Double
163+
```
164+
165+
Несмотря на то, что мы абстрагировались от `Logarithm`, абстракция предоставляется бесплатно:
166+
поскольку существует только одна реализация, во время выполнения не будет накладных расходов
167+
на упаковку для примитивных типов, таких как `Double`.
168+
169+
### Обзор непрозрачных типов
170+
171+
Непрозрачные типы предлагают надежную абстракцию над деталями реализации, не накладывая расходов на производительность.
172+
Как показано выше, непрозрачные типы удобны в использовании и очень хорошо интегрируются с [функцией методов расширения][extension].
173+
174+
[extension]: {% link _overviews/scala3-book/ca-extension-methods.md %}

0 commit comments

Comments
 (0)