diff --git a/_overviews/scala3-book/control-structures.md b/_overviews/scala3-book/control-structures.md
index 61a315ffab..ce62617865 100644
--- a/_overviews/scala3-book/control-structures.md
+++ b/_overviews/scala3-book/control-structures.md
@@ -2,7 +2,7 @@
title: Control Structures
type: chapter
description: This page provides an introduction to Scala's control structures, including if/then/else, 'for' loops, 'for' expressions, 'match' expressions, try/catch/finally, and 'while' loops.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 18
previous-page: first-look-at-types
next-page: domain-modeling-intro
diff --git a/_overviews/scala3-book/domain-modeling-fp.md b/_overviews/scala3-book/domain-modeling-fp.md
index 669f5269eb..f141548bb0 100644
--- a/_overviews/scala3-book/domain-modeling-fp.md
+++ b/_overviews/scala3-book/domain-modeling-fp.md
@@ -2,7 +2,7 @@
title: FP Modeling
type: section
description: This chapter provides an introduction to FP domain modeling with Scala 3.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 22
previous-page: domain-modeling-oop
next-page: methods-intro
diff --git a/_overviews/scala3-book/domain-modeling-intro.md b/_overviews/scala3-book/domain-modeling-intro.md
index 1089997a9b..578cf001df 100644
--- a/_overviews/scala3-book/domain-modeling-intro.md
+++ b/_overviews/scala3-book/domain-modeling-intro.md
@@ -2,7 +2,7 @@
title: Domain Modeling
type: chapter
description: This chapter provides an introduction to domain modeling in Scala 3.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 19
previous-page: control-structures
next-page: domain-modeling-tools
diff --git a/_overviews/scala3-book/domain-modeling-oop.md b/_overviews/scala3-book/domain-modeling-oop.md
index 304dca159b..8a55131a20 100644
--- a/_overviews/scala3-book/domain-modeling-oop.md
+++ b/_overviews/scala3-book/domain-modeling-oop.md
@@ -2,7 +2,7 @@
title: OOP Modeling
type: section
description: This chapter provides an introduction to OOP domain modeling with Scala 3.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 21
previous-page: domain-modeling-tools
next-page: domain-modeling-fp
diff --git a/_overviews/scala3-book/domain-modeling-tools.md b/_overviews/scala3-book/domain-modeling-tools.md
index 2c485e3a5f..a5c02b2a49 100644
--- a/_overviews/scala3-book/domain-modeling-tools.md
+++ b/_overviews/scala3-book/domain-modeling-tools.md
@@ -2,7 +2,7 @@
title: Tools
type: section
description: This chapter provides an introduction to the available domain modeling tools in Scala 3, including classes, traits, enums, and more.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 20
previous-page: domain-modeling-intro
next-page: domain-modeling-oop
diff --git a/_overviews/scala3-book/fun-anonymous-functions.md b/_overviews/scala3-book/fun-anonymous-functions.md
index fbf589e7a3..faf32eb76d 100644
--- a/_overviews/scala3-book/fun-anonymous-functions.md
+++ b/_overviews/scala3-book/fun-anonymous-functions.md
@@ -2,7 +2,7 @@
title: Anonymous Functions
type: section
description: This page shows how to use anonymous functions in Scala, including examples with the List class 'map' and 'filter' functions.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 28
previous-page: fun-intro
next-page: fun-function-variables
diff --git a/_overviews/scala3-book/fun-eta-expansion.md b/_overviews/scala3-book/fun-eta-expansion.md
index e9590a65f0..ab483c5d48 100644
--- a/_overviews/scala3-book/fun-eta-expansion.md
+++ b/_overviews/scala3-book/fun-eta-expansion.md
@@ -2,7 +2,7 @@
title: Eta Expansion
type: section
description: This page discusses Eta Expansion, the Scala technology that automatically and transparently converts methods into functions.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 30
previous-page: fun-function-variables
next-page: fun-hofs
diff --git a/_overviews/scala3-book/fun-function-variables.md b/_overviews/scala3-book/fun-function-variables.md
index 29d1a755df..70805a5240 100644
--- a/_overviews/scala3-book/fun-function-variables.md
+++ b/_overviews/scala3-book/fun-function-variables.md
@@ -1,8 +1,8 @@
---
title: Function Variables
type: section
-description: This page shows how to use anonymous functions in Scala, including examples with the List class 'map' and 'filter' functions.
-languages: [zh-cn]
+description: This page shows how to use function variables in Scala.
+languages: [ru, zh-cn]
num: 29
previous-page: fun-anonymous-functions
next-page: fun-eta-expansion
diff --git a/_overviews/scala3-book/fun-hofs.md b/_overviews/scala3-book/fun-hofs.md
index 484193a4c2..90ebba7396 100644
--- a/_overviews/scala3-book/fun-hofs.md
+++ b/_overviews/scala3-book/fun-hofs.md
@@ -2,7 +2,7 @@
title: Higher-Order Functions
type: section
description: This page demonstrates how to create and use higher-order functions in Scala.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 31
previous-page: fun-eta-expansion
next-page: fun-write-map-function
diff --git a/_overviews/scala3-book/fun-intro.md b/_overviews/scala3-book/fun-intro.md
index e2b9cd798f..aa4345a188 100644
--- a/_overviews/scala3-book/fun-intro.md
+++ b/_overviews/scala3-book/fun-intro.md
@@ -2,7 +2,7 @@
title: Functions
type: chapter
description: This chapter looks at all topics related to functions in Scala 3.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 27
previous-page: methods-summary
next-page: fun-anonymous-functions
diff --git a/_overviews/scala3-book/fun-summary.md b/_overviews/scala3-book/fun-summary.md
index 4b23367b47..99089f5dec 100644
--- a/_overviews/scala3-book/fun-summary.md
+++ b/_overviews/scala3-book/fun-summary.md
@@ -1,8 +1,8 @@
---
title: Summary
type: section
-description: This page shows how to use anonymous functions in Scala, including examples with the List class 'map' and 'filter' functions.
-languages: [zh-cn]
+description: This page provides a summary of the previous 'Functions' sections.
+languages: [ru, zh-cn]
num: 34
previous-page: fun-write-method-returns-function
next-page: packaging-imports
diff --git a/_overviews/scala3-book/fun-write-map-function.md b/_overviews/scala3-book/fun-write-map-function.md
index bc3114fcf3..c7c9234760 100644
--- a/_overviews/scala3-book/fun-write-map-function.md
+++ b/_overviews/scala3-book/fun-write-map-function.md
@@ -2,7 +2,7 @@
title: Write Your Own map Method
type: section
description: This page demonstrates how to create and use higher-order functions in Scala.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 32
previous-page: fun-hofs
next-page: fun-write-method-returns-function
diff --git a/_overviews/scala3-book/fun-write-method-returns-function.md b/_overviews/scala3-book/fun-write-method-returns-function.md
index 6ca9f31002..eb2bb039a1 100644
--- a/_overviews/scala3-book/fun-write-method-returns-function.md
+++ b/_overviews/scala3-book/fun-write-method-returns-function.md
@@ -2,7 +2,7 @@
title: Creating a Method That Returns a Function
type: section
description: This page demonstrates how to create and use higher-order functions in Scala.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 33
previous-page: fun-write-map-function
next-page: fun-summary
diff --git a/_overviews/scala3-book/methods-intro.md b/_overviews/scala3-book/methods-intro.md
index 9170ec39b2..2bf6be1247 100644
--- a/_overviews/scala3-book/methods-intro.md
+++ b/_overviews/scala3-book/methods-intro.md
@@ -2,7 +2,7 @@
title: Methods
type: chapter
description: This section introduces methods in Scala 3.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 23
previous-page: domain-modeling-fp
next-page: methods-most
diff --git a/_overviews/scala3-book/methods-main-methods.md b/_overviews/scala3-book/methods-main-methods.md
index acc0cc1c0b..3415ef3496 100644
--- a/_overviews/scala3-book/methods-main-methods.md
+++ b/_overviews/scala3-book/methods-main-methods.md
@@ -2,7 +2,7 @@
title: Main Methods in Scala 3
type: section
description: This page describes how 'main' methods and the '@main' annotation work in Scala 3.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 25
previous-page: methods-most
next-page: methods-summary
diff --git a/_overviews/scala3-book/methods-most.md b/_overviews/scala3-book/methods-most.md
index 3551a96fe0..a1d17c318e 100644
--- a/_overviews/scala3-book/methods-most.md
+++ b/_overviews/scala3-book/methods-most.md
@@ -2,7 +2,7 @@
title: Method Features
type: section
description: This section introduces Scala 3 methods, including main methods, extension methods, and more.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 24
previous-page: methods-intro
next-page: methods-main-methods
diff --git a/_overviews/scala3-book/methods-summary.md b/_overviews/scala3-book/methods-summary.md
index 1148e37160..54abeef761 100644
--- a/_overviews/scala3-book/methods-summary.md
+++ b/_overviews/scala3-book/methods-summary.md
@@ -2,7 +2,7 @@
title: Summary
type: section
description: This section summarizes the previous sections on Scala 3 methods.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 26
previous-page: methods-main-methods
next-page: fun-intro
diff --git a/_ru/scala3/book/control-structures.md b/_ru/scala3/book/control-structures.md
new file mode 100644
index 0000000000..b72b476278
--- /dev/null
+++ b/_ru/scala3/book/control-structures.md
@@ -0,0 +1,1016 @@
+---
+layout: multipage-overview
+title: Структуры управления
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: chapter
+description: На этой странице представлено введение в структуры управления Scala, включая if/then/else, циклы for, выражения for, выражения match, try/catch/finally и циклы while.
+language: ru
+num: 18
+previous-page: first-look-at-types
+next-page: domain-modeling-intro
+---
+
+
+В Scala есть все структуры управления, которые вы ожидаете найти в языке программирования, в том числе:
+
+- `if`/`then`/`else`
+- циклы `for`
+- циклы `while`
+- `try`/`catch`/`finally`
+
+Здесь также есть две другие мощные конструкции, которые вы, возможно, не видели раньше,
+в зависимости от вашего опыта программирования:
+
+- `for` выражения (также известные как _`for` comprehensions_)
+- `match` выражения
+
+Все они продемонстрированы в следующих разделах.
+
+
+## Конструкция if/then/else
+
+Однострочный Scala оператор `if` выглядит так:
+
+{% tabs control-structures-1 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-1 %}
+```scala
+if (x == 1) println(x)
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-1 %}
+```scala
+if x == 1 then println(x)
+```
+{% endtab %}
+{% endtabs %}
+
+Когда необходимо выполнить несколько строк кода после `if`, используется синтаксис:
+
+{% tabs control-structures-2 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-2 %}
+```scala
+if (x == 1) {
+ println("x is 1, as you can see:")
+ println(x)
+}
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-2 %}
+```scala
+if x == 1 then
+ println("x is 1, as you can see:")
+ println(x)
+```
+{% endtab %}
+{% endtabs %}
+
+`if`/`else` синтаксис выглядит так:
+
+{% tabs control-structures-3 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-3 %}
+```scala
+if (x == 1) {
+ println("x is 1, as you can see:")
+ println(x)
+} else {
+ println("x was not 1")
+}
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-3 %}
+```scala
+if x == 1 then
+ println("x is 1, as you can see:")
+ println(x)
+else
+ println("x was not 1")
+```
+{% endtab %}
+{% endtabs %}
+
+А это синтаксис `if`/`else if`/`else`:
+
+{% tabs control-structures-4 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-4 %}
+```scala
+if (x < 0)
+ println("negative")
+else if (x == 0)
+ println("zero")
+else
+ println("positive")
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-4 %}
+```scala
+if x < 0 then
+ println("negative")
+else if x == 0 then
+ println("zero")
+else
+ println("positive")
+```
+{% endtab %}
+{% endtabs %}
+
+### Утверждение `end if`
+
+
+ Это новое в Scala 3 и не поддерживается в Scala 2.
+
+
+При желании можно дополнительно включить оператор `end if` в конце каждого выражения:
+```scala
+if x == 1 then
+ println("x is 1, as you can see:")
+ println(x)
+end if
+```
+
+### `if`/`else` выражения всегда возвращают результат
+
+Сравнения `if`/`else` образуют _выражения_ - это означает, что они возвращают значение, которое можно присвоить переменной.
+Поэтому нет необходимости в специальном тернарном операторе:
+
+{% tabs control-structures-6 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-6 %}
+```scala
+val minValue = if (a < b) a else b
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-6 %}
+```scala
+val minValue = if a < b then a else b
+```
+{% endtab %}
+{% endtabs %}
+
+
+Поскольку эти выражения возвращают значение, то выражения `if`/`else` можно использовать в качестве тела метода:
+
+{% tabs control-structures-7 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-7 %}
+```scala
+def compare(a: Int, b: Int): Int =
+ if (a < b)
+ -1
+ else if (a == b)
+ 0
+ else
+ 1
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-7 %}
+```scala
+def compare(a: Int, b: Int): Int =
+ if a < b then
+ -1
+ else if a == b then
+ 0
+ else
+ 1
+```
+{% endtab %}
+{% endtabs %}
+
+### В сторону: программирование, ориентированное на выражения
+
+Кратко о программировании в целом: когда каждое написанное вами выражение возвращает значение,
+такой стиль называется _программированием, ориентированным на выражения_,
+или EOP (_expression-oriented programming_).
+Например, это _выражение_:
+
+{% tabs control-structures-8 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-8 %}
+```scala
+val minValue = if (a < b) a else b
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-8 %}
+```scala
+val minValue = if a < b then a else b
+```
+{% endtab %}
+{% endtabs %}
+
+И наоборот, строки кода, которые не возвращают значения, называются _операторами_
+и используются для получения _побочных эффектов_.
+Например, эти строки кода не возвращают значения, поэтому они используются для побочных эффектов:
+
+{% tabs control-structures-9 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-9 %}
+```scala
+if (a == b) action()
+println("Hello")
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-9 %}
+```scala
+if a == b then action()
+println("Hello")
+```
+{% endtab %}
+{% endtabs %}
+
+В первом примере метод `action` запускается как побочный эффект, когда `a` равно `b`.
+Второй пример используется для побочного эффекта печати строки в STDOUT.
+Когда вы узнаете больше о Scala, то обнаружите, что пишете больше _выражений_ и меньше _операторов_.
+
+## Циклы `for`
+
+В самом простом случае цикл `for` в Scala можно использовать для перебора элементов в коллекции.
+Например, имея последовательность целых чисел,
+вы можете перебрать ее элементы и вывести их значения следующим образом:
+
+{% tabs control-structures-10 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-10 %}
+```scala
+val ints = Seq(1, 2, 3)
+for (i <- ints) println(i)
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-10 %}
+```scala
+val ints = Seq(1, 2, 3)
+for i <- ints do println(i)
+```
+{% endtab %}
+{% endtabs %}
+
+
+Код `i <- ints` называется _генератором_.
+
+Вот как выглядит результат в Scala REPL:
+
+{% tabs control-structures-11 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-11 %}
+````
+scala> val ints = Seq(1,2,3)
+ints: Seq[Int] = List(1, 2, 3)
+
+scala> for (i <- ints) println(i)
+1
+2
+3
+````
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-11 %}
+````
+scala> val ints = Seq(1,2,3)
+ints: Seq[Int] = List(1, 2, 3)
+
+scala> for i <- ints do println(i)
+1
+2
+3
+````
+{% endtab %}
+{% endtabs %}
+
+
+Если вам нужен многострочный блок кода после генератора `for`, используйте следующий синтаксис:
+
+{% tabs control-structures-12 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-12 %}
+```scala
+for (i <- ints) {
+ val x = i * 2
+ println(s"i = $i, x = $x")
+}
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-12 %}
+```scala
+for i <- ints
+do
+ val x = i * 2
+ println(s"i = $i, x = $x")
+```
+{% endtab %}
+{% endtabs %}
+
+
+### Несколько генераторов
+
+Циклы `for` могут иметь несколько генераторов, как показано в этом примере:
+
+{% tabs control-structures-13 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-13 %}
+```scala
+for {
+ i <- 1 to 2
+ j <- 'a' to 'b'
+ k <- 1 to 10 by 5
+} {
+ println(s"i = $i, j = $j, k = $k")
+}
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-13 %}
+```scala
+for
+ i <- 1 to 2
+ j <- 'a' to 'b'
+ k <- 1 to 10 by 5
+do
+ println(s"i = $i, j = $j, k = $k")
+```
+{% endtab %}
+{% endtabs %}
+
+
+Это выражение выводит следующее:
+
+````
+i = 1, j = a, k = 1
+i = 1, j = a, k = 6
+i = 1, j = b, k = 1
+i = 1, j = b, k = 6
+i = 2, j = a, k = 1
+i = 2, j = a, k = 6
+i = 2, j = b, k = 1
+i = 2, j = b, k = 6
+````
+
+### "Стражники"
+
+Циклы `for` также могут содержать операторы `if`, известные как _стражники_ (_guards_):
+
+{% tabs control-structures-14 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-14 %}
+```scala
+for {
+ i <- 1 to 5
+ if i % 2 == 0
+} {
+ println(i)
+}
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-14 %}
+```scala
+for
+ i <- 1 to 5
+ if i % 2 == 0
+do
+ println(i)
+```
+{% endtab %}
+{% endtabs %}
+
+
+Результат этого цикла:
+
+````
+2
+4
+````
+
+Цикл `for` может содержать столько стражников, сколько необходимо.
+В этом примере показан один из способов печати числа `4`:
+
+{% tabs control-structures-15 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-15 %}
+```scala
+for {
+ i <- 1 to 10
+ if i > 3
+ if i < 6
+ if i % 2 == 0
+} {
+ println(i)
+}
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-15 %}
+```scala
+for
+ i <- 1 to 10
+ if i > 3
+ if i < 6
+ if i % 2 == 0
+do
+ println(i)
+```
+{% endtab %}
+{% endtabs %}
+
+### Использование `for` с Maps
+
+Вы также можете использовать циклы `for` с `Map`.
+Например, если задана такая `Map` с аббревиатурами штатов и их полными названиями:
+
+{% tabs map %}
+{% tab 'Scala 2 и 3' for=map %}
+```scala
+val states = Map(
+ "AK" -> "Alaska",
+ "AL" -> "Alabama",
+ "AR" -> "Arizona"
+)
+```
+{% endtab %}
+{% endtabs %}
+
+, то можно распечатать ключи и значения, используя `for`. Например:
+
+{% tabs control-structures-16 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-16 %}
+```scala
+for ((abbrev, fullName) <- states) println(s"$abbrev: $fullName")
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-16 %}
+```scala
+for (abbrev, fullName) <- states do println(s"$abbrev: $fullName")
+```
+{% endtab %}
+{% endtabs %}
+
+Вот как это выглядит в REPL:
+
+{% tabs control-structures-17 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-17 %}
+```scala
+scala> for ((abbrev, fullName) <- states) println(s"$abbrev: $fullName")
+AK: Alaska
+AL: Alabama
+AR: Arizona
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-17 %}
+```scala
+scala> for (abbrev, fullName) <- states do println(s"$abbrev: $fullName")
+AK: Alaska
+AL: Alabama
+AR: Arizona
+```
+{% endtab %}
+{% endtabs %}
+
+Когда цикл `for` перебирает карту, каждая пара ключ/значение привязывается
+к переменным `abbrev` и `fullName`, которые находятся в кортеже:
+
+```scala
+(abbrev, fullName) <- states
+```
+
+По мере выполнения цикла переменная `abbrev` принимает значение текущего _ключа_ в карте,
+а переменная `fullName` - соответствующему ключу _значению_.
+
+## Выражение `for`
+
+В предыдущих примерах циклов `for` все эти циклы использовались для _побочных эффектов_,
+в частности для вывода этих значений в STDOUT с помощью `println`.
+
+Важно знать, что вы также можете создавать _выражения_ `for`, которые возвращают значения.
+Вы создаете выражение `for`, добавляя ключевое слово `yield` и возвращаемое выражение, например:
+
+{% tabs control-structures-18 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-18 %}
+```scala
+val list =
+ for (i <- 10 to 12)
+ yield i * 2
+
+// list: IndexedSeq[Int] = Vector(20, 22, 24)
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-18 %}
+```scala
+val list =
+ for i <- 10 to 12
+ yield i * 2
+
+// list: IndexedSeq[Int] = Vector(20, 22, 24)
+```
+{% endtab %}
+{% endtabs %}
+
+
+После выполнения этого выражения `for` переменная `list` содержит `Vector` с отображаемыми значениями.
+Вот как работает выражение:
+
+1. Выражение `for` начинает перебирать значения в диапазоне `(10, 11, 12)`.
+ Сначала оно работает со значением `10`, умножает его на `2`, затем выдает результат - `20`.
+2. Далее берет `11` — второе значение в диапазоне. Умножает его на `2`, а затем выдает значение `22`.
+ Можно представить эти полученные значения как накопление во временном хранилище.
+3. Наконец, цикл берет число `12` из диапазона, умножает его на `2`, получая число `24`.
+ Цикл завершается в этой точке и выдает конечный результат - `Vector(20, 22, 24)`.
+
+Хотя целью этого раздела является демонстрация выражений `for`, полезно знать,
+что показанное выражение `for` эквивалентно вызову метода `map`:
+
+{% tabs map-call %}
+{% tab 'Scala 2 и 3' for=map-call %}
+```scala
+val list = (10 to 12).map(i => i * 2)
+```
+{% endtab %}
+{% endtabs %}
+
+Выражения `for` можно использовать в любой момент, когда вам нужно просмотреть все элементы в коллекции
+и применить алгоритм к этим элементам для создания нового списка.
+
+Вот пример, который показывает, как использовать блок кода после `yield`:
+
+{% tabs control-structures-19 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-19 %}
+```scala
+val names = List("_olivia", "_walter", "_peter")
+
+val capNames = for (name <- names) yield {
+ val nameWithoutUnderscore = name.drop(1)
+ val capName = nameWithoutUnderscore.capitalize
+ capName
+}
+
+// capNames: List[String] = List(Olivia, Walter, Peter)
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-19 %}
+```scala
+val names = List("_olivia", "_walter", "_peter")
+
+val capNames = for name <- names yield
+ val nameWithoutUnderscore = name.drop(1)
+ val capName = nameWithoutUnderscore.capitalize
+ capName
+
+// capNames: List[String] = List(Olivia, Walter, Peter)
+```
+{% endtab %}
+{% endtabs %}
+
+### Использование выражения `for` в качестве тела метода
+
+
+Поскольку выражение `for` возвращает результат, его можно использовать в качестве тела метода,
+который возвращает полезное значение.
+Этот метод возвращает все значения в заданном списке целых чисел, которые находятся между `3` и `10`:
+
+{% tabs control-structures-20 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-20 %}
+```scala
+def between3and10(xs: List[Int]): List[Int] =
+ for {
+ x <- xs
+ if x >= 3
+ if x <= 10
+ } yield x
+
+between3and10(List(1, 3, 7, 11)) // : List[Int] = List(3, 7)
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-20 %}
+```scala
+def between3and10(xs: List[Int]): List[Int] =
+ for
+ x <- xs
+ if x >= 3
+ if x <= 10
+ yield x
+
+between3and10(List(1, 3, 7, 11)) // : List[Int] = List(3, 7)
+```
+{% endtab %}
+{% endtabs %}
+
+## Циклы `while`
+
+Синтаксис цикла `while` в Scala выглядит следующим образом:
+
+{% tabs control-structures-21 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-21 %}
+```scala
+var i = 0
+
+while (i < 3) {
+ println(i)
+ i += 1
+}
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-21 %}
+```scala
+var i = 0
+
+while i < 3 do
+ println(i)
+ i += 1
+```
+{% endtab %}
+{% endtabs %}
+
+## `match` выражения
+
+Сопоставление с образцом (_pattern matching_) является основой функциональных языков программирования.
+Scala включает в себя pattern matching, обладающий множеством возможностей.
+
+В самом простом случае можно использовать выражение `match`, подобное оператору Java `switch`,
+сопоставляя на основе целочисленного значения.
+Как и предыдущие структуры, сопоставление с образцом - это действительно выражение, поскольку оно вычисляет результат:
+
+{% tabs control-structures-22 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-22 %}
+```scala
+// `i` is an integer
+val day = i match {
+ case 0 => "Sunday"
+ case 1 => "Monday"
+ case 2 => "Tuesday"
+ case 3 => "Wednesday"
+ case 4 => "Thursday"
+ case 5 => "Friday"
+ case 6 => "Saturday"
+ case _ => "invalid day" // по умолчанию, перехватывает остальное
+}
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-22 %}
+```scala
+// `i` is an integer
+val day = i match
+ case 0 => "Sunday"
+ case 1 => "Monday"
+ case 2 => "Tuesday"
+ case 3 => "Wednesday"
+ case 4 => "Thursday"
+ case 5 => "Friday"
+ case 6 => "Saturday"
+ case _ => "invalid day" // по умолчанию, перехватывает остальное
+```
+{% endtab %}
+{% endtabs %}
+
+
+В этом примере переменная `i` сопоставляется с заданными числами.
+Если она находится между `0` и `6`, `day` принимает значение строки, представляющей день недели.
+В противном случае она соответствует подстановочному знаку, представленному символом `_`,
+и `day` принимает значение строки `"invalid day"`.
+
+Поскольку сопоставляемые значения рассматриваются в том порядке, в котором они заданы,
+и используется первое совпадение,
+случай по умолчанию, соответствующий любому значению, должен идти последним.
+Любые сопоставляемые случаи после значения по умолчанию будут помечены как недоступные и будет выведено предупреждение.
+
+> При написании простых выражений соответствия, подобных этому, рекомендуется использовать аннотацию `@switch` для переменной `i`.
+> Эта аннотация содержит предупреждение во время компиляции, если switch не может быть скомпилирован в `tableswitch`
+> или `lookupswitch`, которые лучше подходят с точки зрения производительности.
+
+
+### Использование значения по умолчанию
+
+Когда необходимо получить доступ к универсальному значению по умолчанию в `match` выражении,
+просто укажите вместо `_` имя переменной в левой части оператора `case`,
+а затем используйте это имя переменной в правой части оператора при необходимости:
+
+{% tabs control-structures-23 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-23 %}
+```scala
+i match {
+ case 0 => println("1")
+ case 1 => println("2")
+ case what => println(s"You gave me: $what")
+}
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-23 %}
+```scala
+i match
+ case 0 => println("1")
+ case 1 => println("2")
+ case what => println(s"You gave me: $what")
+```
+{% endtab %}
+{% endtabs %}
+
+Имя, используемое в шаблоне, должно начинаться со строчной буквы.
+Имя, начинающееся с заглавной буквы, не представляет собой новую переменную,
+но соответствует значению в области видимости:
+
+{% tabs control-structures-24 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-24 %}
+```scala
+val N = 42
+i match {
+ case 0 => println("1")
+ case 1 => println("2")
+ case N => println("42")
+ case n => println(s"You gave me: $n" )
+}
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-24 %}
+```scala
+val N = 42
+i match
+ case 0 => println("1")
+ case 1 => println("2")
+ case N => println("42")
+ case n => println(s"You gave me: $n" )
+```
+{% endtab %}
+{% endtabs %}
+
+Если `i` равно `42`, то оно будет соответствовать `case N` и напечатает строку `"42"`.
+И не достигнет случая по умолчанию.
+
+### Обработка нескольких возможных совпадений в одной строке
+
+Как уже упоминалось, `match` выражения многофункциональны.
+В этом примере показано, как в каждом операторе `case` использовать несколько возможных совпадений:
+
+{% tabs control-structures-25 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-25 %}
+```scala
+val evenOrOdd = i match {
+ case 1 | 3 | 5 | 7 | 9 => println("odd")
+ case 2 | 4 | 6 | 8 | 10 => println("even")
+ case _ => println("some other number")
+}
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-25 %}
+```scala
+val evenOrOdd = i match
+ case 1 | 3 | 5 | 7 | 9 => println("odd")
+ case 2 | 4 | 6 | 8 | 10 => println("even")
+ case _ => println("some other number")
+```
+{% endtab %}
+{% endtabs %}
+
+### Использование `if` стражников в `case` предложениях
+
+В case выражениях также можно использовать стражников.
+В этом примере второй и третий case используют стражников для сопоставления нескольких целочисленных значений:
+
+{% tabs control-structures-26 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-26 %}
+```scala
+i match {
+ case 1 => println("one, a lonely number")
+ case x if x == 2 || x == 3 => println("two’s company, three’s a crowd")
+ case x if x > 3 => println("4+, that’s a party")
+ case _ => println("i’m guessing your number is zero or less")
+}
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-26 %}
+```scala
+i match
+ case 1 => println("one, a lonely number")
+ case x if x == 2 || x == 3 => println("two’s company, three’s a crowd")
+ case x if x > 3 => println("4+, that’s a party")
+ case _ => println("i’m guessing your number is zero or less")
+```
+{% endtab %}
+{% endtabs %}
+
+
+Вот еще один пример, который показывает, как сопоставить заданное значение с диапазоном чисел:
+
+{% tabs control-structures-27 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-27 %}
+```scala
+i match {
+ case a if 0 to 9 contains a => println(s"0-9 range: $a")
+ case b if 10 to 19 contains b => println(s"10-19 range: $b")
+ case c if 20 to 29 contains c => println(s"20-29 range: $c")
+ case _ => println("Hmmm...")
+}
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-27 %}
+```scala
+i match
+ case a if 0 to 9 contains a => println(s"0-9 range: $a")
+ case b if 10 to 19 contains b => println(s"10-19 range: $b")
+ case c if 20 to 29 contains c => println(s"20-29 range: $c")
+ case _ => println("Hmmm...")
+```
+{% endtab %}
+{% endtabs %}
+
+
+#### Case классы и сопоставление с образцом
+
+Вы также можете извлекать поля из `case` классов — и классов, которые имеют корректно написанные методы `apply`/`unapply` —
+и использовать их в своих условиях.
+Вот пример использования простого case класса `Person`:
+
+{% tabs control-structures-28 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-28 %}
+```scala
+case class Person(name: String)
+
+def speak(p: Person) = p match {
+ case Person(name) if name == "Fred" => println(s"$name says, Yubba dubba doo")
+ case Person(name) if name == "Bam Bam" => println(s"$name says, Bam bam!")
+ case _ => println("Watch the Flintstones!")
+}
+
+speak(Person("Fred")) // "Fred says, Yubba dubba doo"
+speak(Person("Bam Bam")) // "Bam Bam says, Bam bam!"
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-28 %}
+```scala
+case class Person(name: String)
+
+def speak(p: Person) = p match
+ case Person(name) if name == "Fred" => println(s"$name says, Yubba dubba doo")
+ case Person(name) if name == "Bam Bam" => println(s"$name says, Bam bam!")
+ case _ => println("Watch the Flintstones!")
+
+speak(Person("Fred")) // "Fred says, Yubba dubba doo"
+speak(Person("Bam Bam")) // "Bam Bam says, Bam bam!"
+```
+{% endtab %}
+{% endtabs %}
+
+### Использование `match` выражения в качестве тела метода
+
+Поскольку `match` выражения возвращают значение, их можно использовать в качестве тела метода.
+Метод `isTruthy` принимает в качестве входного параметра значение `Matchable`
+и возвращает `Boolean` на основе сопоставления с образцом:
+
+{% tabs control-structures-29 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-29 %}
+```scala
+def isTruthy(a: Matchable) = a match {
+ case 0 | "" | false => false
+ case _ => true
+}
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-29 %}
+```scala
+def isTruthy(a: Matchable) = a match
+ case 0 | "" | false => false
+ case _ => true
+```
+{% endtab %}
+{% endtabs %}
+
+Входной параметр `a` имеет [тип `Matchable`][matchable], являющийся основой всех типов Scala,
+для которых может выполняться сопоставление с образцом.
+Метод реализован путем сопоставления на входе в метод, обрабатывая два варианта:
+первый проверяет, является ли заданное значение целым числом `0`, пустой строкой или `false` и в этом случае возвращается `false`.
+Для любого другого значения в случае по умолчанию мы возвращаем `true`.
+Примеры ниже показывают, как работает этот метод:
+
+{% tabs is-truthy-call %}
+{% tab 'Scala 2 и 3' for=is-truthy-call %}
+```scala
+isTruthy(0) // false
+isTruthy(false) // false
+isTruthy("") // false
+isTruthy(1) // true
+isTruthy(" ") // true
+isTruthy(2F) // true
+```
+{% endtab %}
+{% endtabs %}
+
+Использование сопоставления с образцом в качестве тела метода очень распространено.
+
+#### Использование различных шаблонов в сопоставлении с образцом
+
+Для выражения `match` можно использовать множество различных шаблонов.
+Например:
+
+- Сравнение с константой (такое как `case 3 =>`)
+- Сравнение с последовательностями (такое как `case List(els : _*) =>`)
+- Сравнение с кортежами (такое как `case (x, y) =>`)
+- Сравнение с конструктором класса (такое как `case Person(first, last) =>`)
+- Сравнение по типу (такое как `case p: Person =>`)
+
+Все эти виды шаблонов показаны в следующем методе `pattern`,
+который принимает входной параметр типа `Matchable` и возвращает `String`:
+
+{% tabs control-structures-30 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-30 %}
+```scala
+def pattern(x: Matchable): String = x match {
+
+ // сравнение с константой
+ case 0 => "zero"
+ case true => "true"
+ case "hello" => "you said 'hello'"
+ case Nil => "an empty List"
+
+ // сравнение с последовательностями
+ case List(0, _, _) => "a 3-element list with 0 as the first element"
+ case List(1, _*) => "list, starts with 1, has any number of elements"
+ case Vector(1, _*) => "vector, starts w/ 1, has any number of elements"
+
+ // сравнение с кортежами
+ case (a, b) => s"got $a and $b"
+ case (a, b, c) => s"got $a, $b, and $c"
+
+ // сравнение с конструктором класса
+ case Person(first, "Alexander") => s"Alexander, first name = $first"
+ case Dog("Zeus") => "found a dog named Zeus"
+
+ // сравнение по типу
+ case s: String => s"got a string: $s"
+ case i: Int => s"got an int: $i"
+ case f: Float => s"got a float: $f"
+ case a: Array[Int] => s"array of int: ${a.mkString(",")}"
+ case as: Array[String] => s"string array: ${as.mkString(",")}"
+ case d: Dog => s"dog: ${d.name}"
+ case list: List[?] => s"got a List: $list"
+ case m: Map[?, ?] => m.toString
+
+ // значение по умолчанию с подстановочным знаком
+ case _ => "Unknown"
+}
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-30 %}
+```scala
+def pattern(x: Matchable): String = x match
+
+ // сравнение с константой
+ case 0 => "zero"
+ case true => "true"
+ case "hello" => "you said 'hello'"
+ case Nil => "an empty List"
+
+ // сравнение с последовательностями
+ case List(0, _, _) => "a 3-element list with 0 as the first element"
+ case List(1, _*) => "list, starts with 1, has any number of elements"
+ case Vector(1, _*) => "vector, starts w/ 1, has any number of elements"
+
+ // сравнение с кортежами
+ case (a, b) => s"got $a and $b"
+ case (a, b, c) => s"got $a, $b, and $c"
+
+ // сравнение с конструктором класса
+ case Person(first, "Alexander") => s"Alexander, first name = $first"
+ case Dog("Zeus") => "found a dog named Zeus"
+
+ // сравнение по типу
+ case s: String => s"got a string: $s"
+ case i: Int => s"got an int: $i"
+ case f: Float => s"got a float: $f"
+ case a: Array[Int] => s"array of int: ${a.mkString(",")}"
+ case as: Array[String] => s"string array: ${as.mkString(",")}"
+ case d: Dog => s"dog: ${d.name}"
+ case list: List[?] => s"got a List: $list"
+ case m: Map[?, ?] => m.toString
+
+ // значение по умолчанию с подстановочным знаком
+ case _ => "Unknown"
+```
+{% endtab %}
+{% endtabs %}
+
+## try/catch/finally
+
+Как и в Java, в Scala есть конструкция `try`/`catch`/`finally`, позволяющая перехватывать исключения и управлять ими.
+Для обеспечения согласованности Scala использует тот же синтаксис, что и выражения `match`,
+и поддерживает сопоставление с образцом для различных возможных исключений.
+
+В следующем примере `openAndReadAFile` - это метод, который выполняет то, что следует из его названия:
+он открывает файл и считывает из него текст, присваивая результат изменяемой переменной `text`:
+
+{% tabs control-structures-31 class=tabs-scala-version %}
+{% tab 'Scala 2' for=control-structures-31 %}
+```scala
+var text = ""
+try {
+ text = openAndReadAFile(filename)
+} catch {
+ case fnf: FileNotFoundException => fnf.printStackTrace()
+ case ioe: IOException => ioe.printStackTrace()
+} finally {
+ // здесь необходимо закрыть ресурсы
+ println("Came to the 'finally' clause.")
+}
+```
+{% endtab %}
+{% tab 'Scala 3' for=control-structures-31 %}
+```scala
+var text = ""
+try
+ text = openAndReadAFile(filename)
+catch
+ case fnf: FileNotFoundException => fnf.printStackTrace()
+ case ioe: IOException => ioe.printStackTrace()
+finally
+ // здесь необходимо закрыть ресурсы
+ println("Came to the 'finally' clause.")
+```
+{% endtab %}
+{% endtabs %}
+
+Предполагая, что метод `openAndReadAFile` использует Java `java.io.*` классы для чтения файла
+и не перехватывает его исключения, попытка открыть и прочитать файл может привести как к `FileNotFoundException`,
+так и к `IOException`, и эти два исключения перехватываются в блоке `catch` этого примера.
+
+[matchable]: {{ site.scala3ref }}/other-new-features/matchable.html
diff --git a/_ru/scala3/book/domain-modeling-fp.md b/_ru/scala3/book/domain-modeling-fp.md
new file mode 100644
index 0000000000..e085a0dc06
--- /dev/null
+++ b/_ru/scala3/book/domain-modeling-fp.md
@@ -0,0 +1,825 @@
+---
+layout: multipage-overview
+title: Моделирование ФП
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: section
+description: В этой главе представлено введение в моделирование предметной области с использованием ФП в Scala 3.
+language: ru
+num: 22
+previous-page: domain-modeling-oop
+next-page: methods-intro
+---
+
+
+В этой главе представлено введение в моделирование предметной области
+с использованием функционального программирования (ФП) в Scala 3.
+При моделировании окружающего нас мира с помощью ФП обычно используются следующие конструкции Scala:
+
+- Перечисления
+- Case классы
+- Trait-ы
+
+> Если вы не знакомы с алгебраическими типами данных (ADT) и их обобщенной версией (GADT),
+> то можете прочитать главу ["Алгебраические типы данных"][adts], прежде чем читать этот раздел.
+
+## Введение
+
+В ФП *данные* и *операции над этими данными* — это две разные вещи; вы не обязаны инкапсулировать их вместе, как в ООП.
+
+Концепция аналогична числовой алгебре.
+Когда вы думаете о целых числах, больших либо равных нулю,
+у вас есть *набор* возможных значений, который выглядит следующим образом:
+
+````
+0, 1, 2 ... Int.MaxValue
+````
+
+Игнорируя деление целых чисел, возможные *операции* над этими значениями такие:
+
+````
++, -, *
+````
+
+Схема ФП реализуется аналогичным образом:
+
+- описывается свой набор значений (данные)
+- описываются операции, которые работают с этими значениями (функции)
+
+> Как будет видно, рассуждения о программах в этом стиле сильно отличаются от объектно-ориентированного программирования.
+> Проще говоря о данных в ФП:
+> Отделение функциональности от данных позволяет проверять свои данные, не беспокоясь о поведении.
+
+В этой главе мы смоделируем данные и операции для “пиццы” в пиццерии.
+Будет показано, как реализовать часть “данных” модели Scala/ФП,
+а затем - несколько различных способов организации операций с этими данными.
+
+## Моделирование данных
+
+В Scala достаточно просто описать модель данных:
+
+- если необходимо смоделировать данные с различными вариантами, то используется конструкция `enum` (или `case object` в Scala 2)
+- если необходимо только сгруппировать сущности (или нужен более детальный контроль), то используются case class-ы
+
+### Описание вариантов
+
+Данные, которые просто состоят из различных вариантов, таких как размер корочки, тип корочки и начинка,
+кратко моделируются с помощью перечислений:
+
+{% tabs data_1 class=tabs-scala-version %}
+{% tab 'Scala 2' for=data_1 %}
+
+В Scala 2 перечисления выражаются комбинацией `sealed class` и нескольких `case object`, которые расширяют класс:
+
+```scala
+sealed abstract class CrustSize
+object CrustSize {
+ case object Small extends CrustSize
+ case object Medium extends CrustSize
+ case object Large extends CrustSize
+}
+
+sealed abstract class CrustType
+object CrustType {
+ case object Thin extends CrustType
+ case object Thick extends CrustType
+ case object Regular extends CrustType
+}
+
+sealed abstract class Topping
+object Topping {
+ case object Cheese extends Topping
+ case object Pepperoni extends Topping
+ case object BlackOlives extends Topping
+ case object GreenOlives extends Topping
+ case object Onions extends Topping
+}
+```
+
+{% endtab %}
+{% tab 'Scala 3' for=data_1 %}
+
+В Scala 3 перечисления кратко выражаются конструкцией `enum`:
+
+```scala
+enum CrustSize:
+ case Small, Medium, Large
+
+enum CrustType:
+ case Thin, Thick, Regular
+
+enum Topping:
+ case Cheese, Pepperoni, BlackOlives, GreenOlives, Onions
+```
+
+{% endtab %}
+{% endtabs %}
+
+> Типы данных, которые описывают различные варианты (например, `CrustSize`),
+> также иногда называются суммированными типами (_sum types_).
+
+### Описание основных данных
+
+Пиццу можно рассматривать как _составной_ контейнер с различными атрибутами, указанными выше.
+Мы можем использовать `case class`, чтобы описать,
+что `Pizza` состоит из `crustSize`, `crustType` и, возможно, нескольких `toppings`:
+
+{% tabs data_2 class=tabs-scala-version %}
+{% tab 'Scala 2' for=data_2 %}
+
+```scala
+import CrustSize._
+import CrustType._
+import Topping._
+
+case class Pizza(
+ crustSize: CrustSize,
+ crustType: CrustType,
+ toppings: Seq[Topping]
+)
+```
+
+{% endtab %}
+{% tab 'Scala 3' for=data_2 %}
+
+```scala
+import CrustSize.*
+import CrustType.*
+import Topping.*
+
+case class Pizza(
+ crustSize: CrustSize,
+ crustType: CrustType,
+ toppings: Seq[Topping]
+)
+```
+
+{% endtab %}
+{% endtabs %}
+
+> Типы данных, объединяющие несколько компонентов (например, `Pizza`), также иногда называют типами продуктов (_product types_).
+
+И все. Это модель данных для системы доставки пиццы в стиле ФП.
+Решение очень лаконично, поскольку оно не требует объединения модели данных с операциями с пиццей.
+Модель данных легко читается, как объявление дизайна для реляционной базы данных.
+Также очень легко создавать значения нашей модели данных и проверять их:
+
+{% tabs data_3 %}
+{% tab 'Scala 2 и 3' for=data_3 %}
+
+```scala
+val myFavPizza = Pizza(Small, Regular, Seq(Cheese, Pepperoni))
+println(myFavPizza.crustType) // печатает Regular
+```
+
+{% endtab %}
+{% endtabs %}
+
+#### Подробнее о модели данных
+
+Таким же образом можно было бы смоделировать всю систему заказа пиццы.
+Вот несколько других `case class`-ов, которые используются для моделирования такой системы:
+
+{% tabs data_4 %}
+{% tab 'Scala 2 и 3' for=data_4 %}
+
+```scala
+case class Address(
+ street1: String,
+ street2: Option[String],
+ city: String,
+ state: String,
+ zipCode: String
+)
+
+case class Customer(
+ name: String,
+ phone: String,
+ address: Address
+)
+
+case class Order(
+ pizzas: Seq[Pizza],
+ customer: Customer
+)
+```
+
+{% endtab %}
+{% endtabs %}
+
+#### “Узкие доменные объекты”
+
+В своей книге *Functional and Reactive Domain Modeling*, Debasish Ghosh утверждает,
+что там, где специалисты по ООП описывают свои классы как “широкие модели предметной области”,
+которые инкапсулируют данные и поведение,
+модели данных ФП можно рассматривать как “узкие объекты предметной области”.
+Это связано с тем, что, как показано выше, модели данных определяются как `case` классы с атрибутами,
+но без поведения, что приводит к коротким и лаконичным структурам данных.
+
+## Моделирование операций
+
+Возникает интересный вопрос: поскольку ФП отделяет данные от операций над этими данными,
+то как эти операции реализуются в Scala?
+
+Ответ на самом деле довольно прост: пишутся функции (или методы), работающие со значениями смоделированных данных.
+Например, можно определить функцию, которая вычисляет цену пиццы.
+
+{% tabs data_5 class=tabs-scala-version %}
+{% tab 'Scala 2' for=data_5 %}
+
+```scala
+def pizzaPrice(p: Pizza): Double = p match {
+ case Pizza(crustSize, crustType, toppings) => {
+ val base = 6.00
+ val crust = crustPrice(crustSize, crustType)
+ val tops = toppings.map(toppingPrice).sum
+ base + crust + tops
+ }
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=data_5 %}
+
+```scala
+def pizzaPrice(p: Pizza): Double = p match
+ case Pizza(crustSize, crustType, toppings) =>
+ val base = 6.00
+ val crust = crustPrice(crustSize, crustType)
+ val tops = toppings.map(toppingPrice).sum
+ base + crust + tops
+```
+
+{% endtab %}
+{% endtabs %}
+
+Можно заметить, что реализация функции просто повторяет форму данных: поскольку `Pizza` является case class-ом,
+используется сопоставление с образцом для извлечения компонентов,
+а затем вызываются вспомогательные функции для вычисления отдельных цен.
+
+{% tabs data_6 class=tabs-scala-version %}
+{% tab 'Scala 2' for=data_6 %}
+
+```scala
+def toppingPrice(t: Topping): Double = t match {
+ case Cheese | Onions => 0.5
+ case Pepperoni | BlackOlives | GreenOlives => 0.75
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=data_6 %}
+
+```scala
+def toppingPrice(t: Topping): Double = t match
+ case Cheese | Onions => 0.5
+ case Pepperoni | BlackOlives | GreenOlives => 0.75
+```
+
+{% endtab %}
+{% endtabs %}
+
+Точно так же, поскольку `Topping` является перечислением,
+используется сопоставление с образцом, чтобы разделить варианты.
+Сыр и лук продаются по 50 центов за штуку, остальные — по 75.
+
+{% tabs data_7 class=tabs-scala-version %}
+{% tab 'Scala 2' for=data_7 %}
+
+```scala
+def crustPrice(s: CrustSize, t: CrustType): Double =
+ (s, t) match {
+ // если размер корочки маленький или средний, тип не важен
+ case (Small | Medium, _) => 0.25
+ case (Large, Thin) => 0.50
+ case (Large, Regular) => 0.75
+ case (Large, Thick) => 1.00
+ }
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=data_7 %}
+
+```scala
+def crustPrice(s: CrustSize, t: CrustType): Double =
+ (s, t) match
+ // если размер корочки маленький или средний, тип не важен
+ case (Small | Medium, _) => 0.25
+ case (Large, Thin) => 0.50
+ case (Large, Regular) => 0.75
+ case (Large, Thick) => 1.00
+```
+
+{% endtab %}
+{% endtabs %}
+
+Чтобы рассчитать цену корки, мы одновременно сопоставляем образцы как по размеру, так и по типу корки.
+
+> Важным моментом во всех показанных выше функциях является то, что они являются чистыми функциями (_pure functions_):
+> они не изменяют данные и не имеют других побочных эффектов (таких, как выдача исключений или запись в файл).
+> Всё, что они делают - это просто получают значения и вычисляют результат.
+
+## Как организовать функциональность?
+
+При реализации функции `pizzaPrice`, описанной выше, не было сказано, _где_ ее определять.
+Scala предоставляет множество отличных инструментов для организации логики в различных пространствах имен и модулях.
+
+Существует несколько способов реализации и организации поведения:
+
+- определить функции в сопутствующих объектах (companion object)
+- использовать модульный стиль программирования
+- использовать подход “функциональных объектов”
+- определить функциональность в методах расширения
+
+Эти различные решения показаны в оставшейся части этого раздела.
+
+### Сопутствующий объект
+
+Первый подход — определить поведение (функции) в сопутствующем объекте.
+
+> Как обсуждалось в разделе [“Инструменты”][modeling-tools],
+> _сопутствующий объект_ — это `object` с тем же именем, что и у класса, и объявленный в том же файле, что и класс.
+
+При таком подходе в дополнение к `enum` или `case class` также определяется сопутствующий объект с таким же именем,
+который содержит поведение (функции).
+
+{% tabs org_1 class=tabs-scala-version %}
+{% tab 'Scala 2' for=org_1 %}
+
+```scala
+case class Pizza(
+ crustSize: CrustSize,
+ crustType: CrustType,
+ toppings: Seq[Topping]
+)
+
+// сопутствующий объект для case class Pizza
+object Pizza {
+ // тоже самое, что и `pizzaPrice`
+ def price(p: Pizza): Double = ...
+}
+
+sealed abstract class Topping
+
+// сопутствующий объект для перечисления Topping
+object Topping {
+ case object Cheese extends Topping
+ case object Pepperoni extends Topping
+ case object BlackOlives extends Topping
+ case object GreenOlives extends Topping
+ case object Onions extends Topping
+
+ // тоже самое, что и `toppingPrice`
+ def price(t: Topping): Double = ...
+}
+```
+
+{% endtab %}
+{% tab 'Scala 3' for=org_1 %}
+
+```scala
+case class Pizza(
+ crustSize: CrustSize,
+ crustType: CrustType,
+ toppings: Seq[Topping]
+)
+
+// сопутствующий объект для case class Pizza
+object Pizza:
+ // тоже самое, что и `pizzaPrice`
+ def price(p: Pizza): Double = ...
+
+enum Topping:
+ case Cheese, Pepperoni, BlackOlives, GreenOlives, Onions
+
+// сопутствующий объект для перечисления Topping
+object Topping:
+ // тоже самое, что и `toppingPrice`
+ def price(t: Topping): Double = ...
+```
+
+{% endtab %}
+{% endtabs %}
+
+При таком подходе можно создать `Pizza` и вычислить ее цену следующим образом:
+
+{% tabs org_2 %}
+{% tab 'Scala 2 и 3' for=org_2 %}
+
+```scala
+val pizza1 = Pizza(Small, Thin, Seq(Cheese, Onions))
+Pizza.price(pizza1)
+```
+
+{% endtab %}
+{% endtabs %}
+
+Группировка функциональности с помощью сопутствующих объектов имеет несколько преимуществ:
+
+- связывает функциональность с данными и облегчает их поиск программистам (и компилятору).
+- создает пространство имен и, например, позволяет использовать `price` в качестве имени метода, не полагаясь на перегрузку.
+- реализация `Topping.price` может получить доступ к значениям перечисления, таким как `Cheese`, без необходимости их импорта.
+
+Однако также есть несколько компромиссов, которые следует учитывать:
+
+- модель данных тесно связывается с функциональностью.
+ В частности, сопутствующий объект должен быть определен в том же файле, что и `case class`.
+- неясно, где определять такие функции, как `crustPrice`,
+ которые с одинаковым успехом можно поместить в сопутствующий объект `CrustSize` или `CrustType`.
+
+## Модули
+
+Второй способ организации поведения — использование “модульного” подхода.
+В книге _“Программирование на Scala”_ _модуль_ определяется как
+“небольшая часть программы с четко определенным интерфейсом и скрытой реализацией”.
+Давайте посмотрим, что это значит.
+
+### Создание интерфейса `PizzaService`
+
+Первое, о чем следует подумать, — это “поведение” `Pizza`.
+Делая это, определяем `trait PizzaServiceInterface` следующим образом:
+
+{% tabs module_1 class=tabs-scala-version %}
+{% tab 'Scala 2' for=module_1 %}
+
+```scala
+trait PizzaServiceInterface {
+
+ def price(p: Pizza): Double
+
+ def addTopping(p: Pizza, t: Topping): Pizza
+ def removeAllToppings(p: Pizza): Pizza
+
+ def updateCrustSize(p: Pizza, cs: CrustSize): Pizza
+ def updateCrustType(p: Pizza, ct: CrustType): Pizza
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=module_1 %}
+
+```scala
+trait PizzaServiceInterface:
+
+ def price(p: Pizza): Double
+
+ def addTopping(p: Pizza, t: Topping): Pizza
+ def removeAllToppings(p: Pizza): Pizza
+
+ def updateCrustSize(p: Pizza, cs: CrustSize): Pizza
+ def updateCrustType(p: Pizza, ct: CrustType): Pizza
+```
+
+{% endtab %}
+{% endtabs %}
+
+Как показано, каждый метод принимает `Pizza` в качестве входного параметра вместе с другими параметрами,
+а затем возвращает экземпляр `Pizza` в качестве результата.
+
+Когда пишется такой чистый интерфейс, можно думать о нем как о контракте,
+в котором говорится: “Все неабстрактные классы, расширяющие этот trait, должны предоставлять реализацию этих сервисов”.
+
+На этом этапе также можно представить, что вы являетесь потребителем этого API.
+Когда вы это сделаете, будет полезно набросать некоторый пример “потребительского” кода,
+чтобы убедиться, что API выглядит так, как хотелось:
+
+{% tabs module_2 %}
+{% tab 'Scala 2 и 3' for=module_2 %}
+
+```scala
+val p = Pizza(Small, Thin, Seq(Cheese))
+
+// как вы хотите использовать методы в PizzaServiceInterface
+val p1 = addTopping(p, Pepperoni)
+val p2 = addTopping(p1, Onions)
+val p3 = updateCrustType(p2, Thick)
+val p4 = updateCrustSize(p3, Large)
+```
+
+{% endtab %}
+{% endtabs %}
+
+Если с этим кодом все в порядке, как правило, можно начать набрасывать другой API, например API для заказов,
+но, поскольку сейчас рассматривается только `Pizza`, перейдем к созданию конкретной реализации этого интерфейса.
+
+> Обратите внимание, что обычно это двухэтапный процесс.
+> На первом шаге набрасывается контракт API в качестве _интерфейса_.
+> На втором шаге создается конкретная _реализация_ этого интерфейса.
+> В некоторых случаях в конечном итоге создается несколько конкретных реализаций базового интерфейса.
+
+### Создание конкретной реализации
+
+Теперь, когда известно, как выглядит `PizzaServiceInterface`, можно создать конкретную реализацию,
+написав тело для всех методов, определенных в интерфейсе:
+
+{% tabs module_3 class=tabs-scala-version %}
+{% tab 'Scala 2' for=module_3 %}
+
+```scala
+object PizzaService extends PizzaServiceInterface {
+
+ def price(p: Pizza): Double =
+ ... // реализация была дана выше
+
+ def addTopping(p: Pizza, t: Topping): Pizza =
+ p.copy(toppings = p.toppings :+ t)
+
+ def removeAllToppings(p: Pizza): Pizza =
+ p.copy(toppings = Seq.empty)
+
+ def updateCrustSize(p: Pizza, cs: CrustSize): Pizza =
+ p.copy(crustSize = cs)
+
+ def updateCrustType(p: Pizza, ct: CrustType): Pizza =
+ p.copy(crustType = ct)
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=module_3 %}
+
+```scala
+object PizzaService extends PizzaServiceInterface:
+
+ def price(p: Pizza): Double =
+ ... // реализация была дана выше
+
+ def addTopping(p: Pizza, t: Topping): Pizza =
+ p.copy(toppings = p.toppings :+ t)
+
+ def removeAllToppings(p: Pizza): Pizza =
+ p.copy(toppings = Seq.empty)
+
+ def updateCrustSize(p: Pizza, cs: CrustSize): Pizza =
+ p.copy(crustSize = cs)
+
+ def updateCrustType(p: Pizza, ct: CrustType): Pizza =
+ p.copy(crustType = ct)
+
+end PizzaService
+```
+
+{% endtab %}
+{% endtabs %}
+
+Хотя двухэтапный процесс создания интерфейса с последующей реализацией не всегда необходим,
+явное продумывание API и его использования — хороший подход.
+
+Когда все готово, можно использовать `Pizza` и `PizzaService`:
+
+{% tabs module_4 class=tabs-scala-version %}
+{% tab 'Scala 2' for=module_4 %}
+
+```scala
+import PizzaService._
+
+val p = Pizza(Small, Thin, Seq(Cheese))
+
+// использование методов PizzaService
+val p1 = addTopping(p, Pepperoni)
+val p2 = addTopping(p1, Onions)
+val p3 = updateCrustType(p2, Thick)
+val p4 = updateCrustSize(p3, Large)
+
+println(price(p4)) // печатает 8.75
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=module_4 %}
+
+```scala
+import PizzaService.*
+
+val p = Pizza(Small, Thin, Seq(Cheese))
+
+// использование методов PizzaService
+val p1 = addTopping(p, Pepperoni)
+val p2 = addTopping(p1, Onions)
+val p3 = updateCrustType(p2, Thick)
+val p4 = updateCrustSize(p3, Large)
+
+println(price(p4)) // печатает 8.75
+```
+
+{% endtab %}
+{% endtabs %}
+
+### Функциональные объекты
+
+В книге _“Программирование на Scala”_ авторы определяют термин “Функциональные объекты” как
+“объекты, которые не имеют никакого изменяемого состояния”.
+Это также относится к типам в `scala.collection.immutable`.
+Например, методы в `List` не изменяют внутреннего состояния, а вместо этого в результате создают копию `List`.
+
+Об этом подходе можно думать, как о “гибридном дизайне ФП/ООП”, потому что:
+
+- данные моделируются, используя неизменяемые `case` классы.
+- определяется поведение (методы) _того же типа_, что и данные.
+- поведение реализуется как чистые функции: они не изменяют никакого внутреннего состояния; скорее - возвращают копию.
+
+> Это действительно гибридный подход: как и в **дизайне ООП**, методы инкапсулированы в класс с данными,
+> но, как это обычно бывает **в дизайне ФП**, методы реализованы как чистые функции, которые данные не изменяют.
+
+#### Пример
+
+Используя этот подход, можно напрямую реализовать функциональность пиццы в `case class`:
+
+{% tabs module_5 class=tabs-scala-version %}
+{% tab 'Scala 2' for=module_5 %}
+
+```scala
+case class Pizza(
+ crustSize: CrustSize,
+ crustType: CrustType,
+ toppings: Seq[Topping]
+) {
+
+ // операции этой модели данных
+ def price: Double =
+ pizzaPrice(this) // такая же имплементация, как и выше
+
+ def addTopping(t: Topping): Pizza =
+ this.copy(toppings = this.toppings :+ t)
+
+ def removeAllToppings: Pizza =
+ this.copy(toppings = Seq.empty)
+
+ def updateCrustSize(cs: CrustSize): Pizza =
+ this.copy(crustSize = cs)
+
+ def updateCrustType(ct: CrustType): Pizza =
+ this.copy(crustType = ct)
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=module_5 %}
+
+```scala
+case class Pizza(
+ crustSize: CrustSize,
+ crustType: CrustType,
+ toppings: Seq[Topping]
+):
+
+ // операции этой модели данных
+ def price: Double =
+ pizzaPrice(this) // такая же имплементация, как и выше
+
+ def addTopping(t: Topping): Pizza =
+ this.copy(toppings = this.toppings :+ t)
+
+ def removeAllToppings: Pizza =
+ this.copy(toppings = Seq.empty)
+
+ def updateCrustSize(cs: CrustSize): Pizza =
+ this.copy(crustSize = cs)
+
+ def updateCrustType(ct: CrustType): Pizza =
+ this.copy(crustType = ct)
+```
+
+{% endtab %}
+{% endtabs %}
+
+Обратите внимание, что в отличие от предыдущих подходов, поскольку это методы класса `Pizza`,
+они не принимают ссылку `Pizza` в качестве входного параметра.
+Вместо этого у них есть собственная ссылка на текущий экземпляр пиццы - `this`.
+
+Теперь можно использовать этот новый дизайн следующим образом:
+
+{% tabs module_6 %}
+{% tab 'Scala 2 и 3' for=module_6 %}
+
+```scala
+Pizza(Small, Thin, Seq(Cheese))
+ .addTopping(Pepperoni)
+ .updateCrustType(Thick)
+ .price
+```
+
+{% endtab %}
+{% endtabs %}
+
+### Методы расширения
+
+Методы расширения - подход, который находится где-то между первым (определение функций в сопутствующем объекте)
+и последним (определение функций как методов самого типа).
+
+Методы расширения позволяют создавать API, похожий на API функционального объекта,
+без необходимости определять функции как методы самого типа.
+Это может иметь несколько преимуществ:
+
+- модель данных снова _очень лаконична_ и не упоминает никакого поведения.
+- можно _задним числом_ развить функциональность типов дополнительными методами, не изменяя исходного определения.
+- помимо сопутствующих объектов или прямых методов типов, методы расширения могут быть определены _извне_ в другом файле.
+
+Вернемся к примеру:
+
+{% tabs module_7 class=tabs-scala-version %}
+{% tab 'Scala 2' for=module_7 %}
+
+```scala
+case class Pizza(
+ crustSize: CrustSize,
+ crustType: CrustType,
+ toppings: Seq[Topping]
+)
+
+implicit class PizzaOps(p: Pizza) {
+ def price: Double =
+ pizzaPrice(p) // такая же имплементация, как и выше
+
+ def addTopping(t: Topping): Pizza =
+ p.copy(toppings = p.toppings :+ t)
+
+ def removeAllToppings: Pizza =
+ p.copy(toppings = Seq.empty)
+
+ def updateCrustSize(cs: CrustSize): Pizza =
+ p.copy(crustSize = cs)
+
+ def updateCrustType(ct: CrustType): Pizza =
+ p.copy(crustType = ct)
+}
+```
+В приведенном выше коде мы определяем различные методы для пиццы как методы в _неявном классе_ (_implicit class_).
+С `implicit class PizzaOps(p: Pizza)` тогда, где бы `PizzaOps` ни был импортирован,
+его методы будут доступны в экземплярах `Pizza`.
+Получатель в этом случае `p`.
+
+{% endtab %}
+{% tab 'Scala 3' for=module_7 %}
+
+```scala
+case class Pizza(
+ crustSize: CrustSize,
+ crustType: CrustType,
+ toppings: Seq[Topping]
+)
+
+extension (p: Pizza)
+ def price: Double =
+ pizzaPrice(p) // такая же имплементация, как и выше
+
+ def addTopping(t: Topping): Pizza =
+ p.copy(toppings = p.toppings :+ t)
+
+ def removeAllToppings: Pizza =
+ p.copy(toppings = Seq.empty)
+
+ def updateCrustSize(cs: CrustSize): Pizza =
+ p.copy(crustSize = cs)
+
+ def updateCrustType(ct: CrustType): Pizza =
+ p.copy(crustType = ct)
+```
+В приведенном выше коде мы определяем различные методы для пиццы как _методы расширения_ (_extension methods_).
+С помощью `extension (p: Pizza)` мы говорим, что хотим сделать методы доступными для экземпляров `Pizza`.
+Получатель в этом случае `p`.
+
+{% endtab %}
+{% endtabs %}
+
+Используя наши методы расширения, мы можем получить тот же API, что и раньше:
+
+{% tabs module_8 %}
+{% tab 'Scala 2 и 3' for=module_8 %}
+
+```scala
+Pizza(Small, Thin, Seq(Cheese))
+ .addTopping(Pepperoni)
+ .updateCrustType(Thick)
+ .price
+```
+
+{% endtab %}
+{% endtabs %}
+
+При этом методы расширения можно определить в любом другом модуле.
+Как правило, если вы являетесь разработчиком модели данных, то определяете свои методы расширения в сопутствующем объекте.
+Таким образом, они уже доступны всем пользователям.
+В противном случае методы расширения должны быть импортированы явно, чтобы их можно было использовать.
+
+## Резюме функционального подхода
+
+Определение модели данных в Scala/ФП, как правило, простое:
+моделируются варианты данных с помощью перечислений и составных данных с помощью `case` классов.
+Затем, чтобы смоделировать поведение, определяются функции, которые работают со значениями модели данных.
+Были рассмотрены разные способы организации функций:
+
+- можно поместить методы в сопутствующие объекты
+- можно использовать модульный стиль программирования, разделяющий интерфейс и реализацию
+- можно использовать подход “функциональных объектов” и хранить методы в определенном типе данных
+- можно использовать методы расширения, чтобы снабдить модель данных функциональностью
+
+[adts]: {% link _overviews/scala3-book/types-adts-gadts.md %}
+[modeling-tools]: {% link _overviews/scala3-book/domain-modeling-tools.md %}
diff --git a/_ru/scala3/book/domain-modeling-intro.md b/_ru/scala3/book/domain-modeling-intro.md
new file mode 100644
index 0000000000..6202dec08d
--- /dev/null
+++ b/_ru/scala3/book/domain-modeling-intro.md
@@ -0,0 +1,19 @@
+---
+layout: multipage-overview
+title: Моделирование предметной области
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: chapter
+description: В этой главе показано, как можно моделировать предметную область с помощью Scala 3.
+language: ru
+num: 19
+previous-page: control-structures
+next-page: domain-modeling-tools
+---
+
+В этой главе показано, как можно смоделировать предметную область с помощью Scala 3:
+
+- В разделе "Инструменты" представлены доступные вам инструменты, включая классы, трейты, перечисления и многое другое.
+- В разделе "Моделирование ООП" рассматриваются атрибуты и поведение моделирования в стиле объектно-ориентированного программирования (ООП).
+- В разделе "Моделирование ФП" рассматривается моделирование предметной области в стиле функционального программирования (ФП).
diff --git a/_ru/scala3/book/domain-modeling-oop.md b/_ru/scala3/book/domain-modeling-oop.md
new file mode 100644
index 0000000000..df09cdbdd2
--- /dev/null
+++ b/_ru/scala3/book/domain-modeling-oop.md
@@ -0,0 +1,602 @@
+---
+layout: multipage-overview
+title: Моделирование ООП
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: section
+description: В этой главе представлено введение в моделирование предметной области с использованием ООП в Scala 3.
+language: ru
+num: 21
+previous-page: domain-modeling-tools
+next-page: domain-modeling-fp
+---
+
+В этой главе представлено введение в моделирование предметной области с использованием
+объектно-ориентированного программирования (ООП) в Scala 3.
+
+## Введение
+
+Scala предоставляет все необходимые инструменты для объектно-ориентированного проектирования:
+
+- **Traits** позволяют указывать (абстрактные) интерфейсы, а также конкретные реализации.
+- **Mixin Composition** предоставляет инструменты для создания компонентов из более мелких деталей.
+- **Классы** могут реализовывать интерфейсы, заданные трейтами.
+- **Экземпляры** классов могут иметь свое собственное приватное состояние.
+- **Subtyping** позволяет использовать экземпляр одного класса там, где ожидается экземпляр его суперкласса.
+- **Модификаторы доступа** позволяют управлять, к каким членам класса можно получить доступ с помощью какой части кода.
+
+## Трейты
+
+В отличие от других языков с поддержкой ООП, таких как Java, возможно,
+основным инструментом декомпозиции в Scala являются не классы, а трейты.
+Они могут служить для описания абстрактных интерфейсов, таких как:
+
+{% tabs traits_1 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+trait Showable {
+ def show: String
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+trait Showable:
+ def show: String
+```
+{% endtab %}
+{% endtabs %}
+
+а также могут содержать конкретные реализации:
+
+{% tabs traits_2 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+trait Showable {
+ def show: String
+ def showHtml = "
"
+```
+{% endtab %}
+{% endtabs %}
+
+На примере видно, что метод `showHtml` определяется в терминах абстрактного метода `show`.
+
+[Odersky и Zenger][scalable] представляют _сервис-ориентированную компонентную модель_ и рассматривают:
+
+- **абстрактные члены** как _требуемые_ службы: их все еще необходимо реализовать в подклассе.
+- **конкретные члены** как _предоставляемые_ услуги: они предоставляются подклассу.
+
+Это видно на примере со `Showable`: определяя класс `Document`, который расширяет `Showable`,
+все еще нужно определить `show`, но `showHtml` уже предоставляется:
+
+{% tabs traits_3 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+class Document(text: String) extends Showable {
+ def show = text
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+class Document(text: String) extends Showable:
+ def show = text
+```
+
+{% endtab %}
+{% endtabs %}
+
+#### Абстрактные члены
+
+Абстрактными в `trait` могут оставаться не только методы.
+`trait` может содержать:
+
+- абстрактные методы (`def m(): T`)
+- абстрактные переменные (`val x: T`)
+- абстрактные типы (`type T`), потенциально с ограничениями (`type T <: S`)
+- абстрактные given (`given t: T`) только в Scala 3
+
+Каждая из вышеперечисленных функций может быть использована для определения той или иной формы требований к реализатору `trait`.
+
+## Смешанная композиция
+
+Кроме того, что `trait`-ы могут содержать абстрактные и конкретные определения,
+Scala также предоставляет мощный способ создания нескольких `trait`:
+структура, которую часто называют _смешанной композицией_.
+
+Предположим, что существуют следующие два (потенциально независимо определенные) `trait`-а:
+
+{% tabs traits_4 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+trait GreetingService {
+ def translate(text: String): String
+ def sayHello = translate("Hello")
+}
+
+trait TranslationService {
+ def translate(text: String): String = "..."
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+trait GreetingService:
+ def translate(text: String): String
+ def sayHello = translate("Hello")
+
+trait TranslationService:
+ def translate(text: String): String = "..."
+```
+
+{% endtab %}
+{% endtabs %}
+
+Чтобы скомпоновать два сервиса, можно просто создать новый `trait`, расширяющий их:
+
+{% tabs traits_5 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+trait ComposedService extends GreetingService with TranslationService
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+trait ComposedService extends GreetingService, TranslationService
+```
+
+{% endtab %}
+{% endtabs %}
+
+Абстрактные элементы в одном `trait`-е (например, `translate` в `GreetingService`)
+автоматически сопоставляются с конкретными элементами в другом `trait`-е.
+Это работает не только с методами, как в этом примере, но и со всеми другими абстрактными членами,
+упомянутыми выше (то есть типами, переменными и т.д.).
+
+## Классы
+
+`trait`-ы отлично подходят для модуляции компонентов и описания интерфейсов (обязательных и предоставляемых).
+Но в какой-то момент возникнет необходимость создавать их экземпляры.
+При разработке программного обеспечения в Scala часто бывает полезно рассмотреть возможность
+использования классов только на начальных этапах модели наследования:
+
+{% tabs table-traits-cls-summary class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+| Трейты | `T1`, `T2`, `T3`
+| Составные трейты | `S1 extends T1 with T2`, `S2 extends T2 with T3`
+| Классы | `C extends S1 with T3`
+| Экземпляры | `new C()`
+{% endtab %}
+{% tab 'Scala 3' %}
+| Трейты | `T1`, `T2`, `T3`
+| Составные трейты | `S1 extends T1, T2`, `S2 extends T2, T3`
+| Классы | `C extends S1, T3`
+| Экземпляры | `C()`
+{% endtab %}
+{% endtabs %}
+
+Это еще более актуально в Scala 3, где трейты теперь также могут принимать параметры конструктора,
+что еще больше устраняет необходимость в классах.
+
+#### Определение класса
+
+Подобно `trait`-ам, классы могут расширять несколько `trait`-ов (но только один суперкласс):
+
+{% tabs class_1 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+class MyService(name: String) extends ComposedService with Showable {
+ def show = s"$name says $sayHello"
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+class MyService(name: String) extends ComposedService, Showable:
+ def show = s"$name says $sayHello"
+```
+
+{% endtab %}
+{% endtabs %}
+
+#### Подтипы
+
+Экземпляр `MyService` создается следующим образом:
+
+{% tabs class_2 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+val s1: MyService = new MyService("Service 1")
+```
+
+{% endtab %}
+{% tab 'Scala 3' %}
+
+```scala
+val s1: MyService = MyService("Service 1")
+```
+
+{% endtab %}
+{% endtabs %}
+
+С помощью подтипов экземпляр `s1` можно использовать везде, где ожидается любое из расширенных свойств:
+
+{% tabs class_3 %}
+{% tab 'Scala 2 и 3' %}
+
+```scala
+val s2: GreetingService = s1
+val s3: TranslationService = s1
+val s4: Showable = s1
+// ... и так далее ...
+```
+{% endtab %}
+{% endtabs %}
+
+#### Планирование расширения
+
+Как упоминалось ранее, можно расширить еще один класс:
+
+{% tabs class_4 %}
+{% tab 'Scala 2 и 3' %}
+
+```scala
+class Person(name: String)
+class SoftwareDeveloper(name: String, favoriteLang: String)
+ extends Person(name)
+```
+
+{% endtab %}
+{% endtabs %}
+
+Однако, поскольку `trait`-ы разработаны как основное средство декомпозиции,
+то не рекомендуется расширять класс, определенный в одном файле, из другого файла.
+
+
Открытые классы только в Scala 3
+
+В Scala 3 расширение неабстрактных классов в других файлах ограничено.
+Чтобы разрешить это, базовый класс должен быть помечен как `open`:
+
+{% tabs class_5 %}
+{% tab 'Только в Scala 3' %}
+
+```scala
+open class Person(name: String)
+```
+{% endtab %}
+{% endtabs %}
+
+Маркировка классов с помощью [`open`][open] - это новая функция Scala 3.
+Необходимость явно помечать классы как открытые позволяет избежать многих распространенных ошибок в ООП.
+В частности, это требует, чтобы разработчики библиотек явно планировали расширение
+и, например, документировали классы, помеченные как открытые.
+
+## Экземпляры и приватное изменяемое состояние
+
+Как и в других языках с поддержкой ООП, трейты и классы в Scala могут определять изменяемые поля:
+
+{% tabs instance_6 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+class Counter {
+ // получить значение можно только с помощью метода `count`
+ private var currentCount = 0
+
+ def tick(): Unit = currentCount += 1
+ def count: Int = currentCount
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+class Counter:
+ // получить значение можно только с помощью метода `count`
+ private var currentCount = 0
+
+ def tick(): Unit = currentCount += 1
+ def count: Int = currentCount
+```
+
+{% endtab %}
+{% endtabs %}
+
+Каждый экземпляр класса `Counter` имеет собственное приватное состояние,
+которое можно получить только через метод `count`, как показано в следующем взаимодействии:
+
+{% tabs instance_7 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+val c1 = new Counter()
+c1.count // 0
+c1.tick()
+c1.tick()
+c1.count // 2
+```
+
+{% endtab %}
+{% tab 'Scala 3' %}
+
+```scala
+val c1 = Counter()
+c1.count // 0
+c1.tick()
+c1.tick()
+c1.count // 2
+```
+
+{% endtab %}
+{% endtabs %}
+
+#### Модификаторы доступа
+
+По умолчанию все определения элементов в Scala общедоступны.
+Чтобы скрыть детали реализации, можно определить элементы (методы, поля, типы и т.д.) в качестве `private` или `protected`.
+Таким образом, вы можете управлять доступом к ним или их переопределением.
+Закрытые (`private`) элементы видны только самому классу/трейту и его сопутствующему объекту.
+Защищенные (`protected`) элементы также видны для подклассов класса.
+
+## Дополнительный пример: сервис-ориентированный дизайн
+
+Далее будут проиллюстрированы некоторые расширенные возможности Scala и показано,
+как их можно использовать для структурирования более крупных программных компонентов.
+Примеры взяты из статьи Мартина Одерски и Маттиаса Зенгера ["Scalable Component Abstractions"][scalable].
+Пример в первую очередь предназначен для демонстрации того,
+как использовать несколько функций типа для создания более крупных компонентов.
+
+Цель состоит в том, чтобы определить программный компонент с семейством типов,
+которые могут быть уточнены позже при реализации компонента.
+Конкретно, следующий код определяет компонент `SubjectObserver` как `trait` с двумя членами абстрактного типа,
+`S` (для субъектов) и `O` (для наблюдателей):
+
+{% tabs example_1 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+trait SubjectObserver {
+
+ type S <: Subject
+ type O <: Observer
+
+ trait Subject { self: S =>
+ private var observers: List[O] = List()
+ def subscribe(obs: O): Unit = {
+ observers = obs :: observers
+ }
+ def publish() = {
+ for ( obs <- observers ) obs.notify(this)
+ }
+ }
+
+ trait Observer {
+ def notify(sub: S): Unit
+ }
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+trait SubjectObserver:
+
+ type S <: Subject
+ type O <: Observer
+
+ trait Subject:
+ self: S =>
+ private var observers: List[O] = List()
+ def subscribe(obs: O): Unit =
+ observers = obs :: observers
+ def publish() =
+ for obs <- observers do obs.notify(this)
+
+ trait Observer:
+ def notify(sub: S): Unit
+```
+
+{% endtab %}
+{% endtabs %}
+
+Есть несколько вещей, которые нуждаются в объяснении.
+
+#### Члены абстрактного типа
+
+Тип объявления `S <: Subject` говорит, что внутри trait `SubjectObserver` можно ссылаться на
+некоторый _неизвестный_ (то есть абстрактный) тип, который называется `S`.
+Однако этот тип не является полностью неизвестным: мы знаем, по крайней мере, что это какой-то подтип `Subject`.
+Все trait-ы и классы, расширяющие `SubjectObserver`, могут свободно выбирать любой тип для `S`,
+если выбранный тип является подтипом `Subject`.
+Часть `<: Subject` декларации также упоминается как верхняя граница на `S`.
+
+#### Вложенные trait-ы
+
+_В рамках_ trait-а `SubjectObserver` определяются два других trait-а.
+trait `Observer`, который определяет только один абстрактный метод `notify` с одним аргументом типа `S`.
+Как будет видно, важно, чтобы аргумент имел тип `S`, а не тип `Subject`.
+
+Второй trait, `Subject`, определяет одно приватное поле `observers` для хранения всех наблюдателей,
+подписавшихся на этот конкретный объект. Подписка на объект просто сохраняет объект в списке.
+Опять же, тип параметра `obs` - это `O`, а не `Observer`.
+
+#### Аннотации собственного типа
+
+Наконец, что означает `self: S =>` в trait-е `Subject`? Это называется аннотацией собственного типа.
+И требует, чтобы подтипы `Subject` также были подтипами `S`.
+Это необходимо, чтобы иметь возможность вызывать `obs.notify` с `this` в качестве аргумента,
+поскольку для этого требуется значение типа `S`.
+Если бы `S` был конкретным типом, аннотацию собственного типа можно было бы заменить на `trait Subject extends S`.
+
+### Реализация компонента
+
+Теперь можно реализовать вышеуказанный компонент и определить члены абстрактного типа как конкретные типы:
+
+{% tabs example_2 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+object SensorReader extends SubjectObserver {
+ type S = Sensor
+ type O = Display
+
+ class Sensor(val label: String) extends Subject {
+ private var currentValue = 0.0
+ def value = currentValue
+ def changeValue(v: Double) = {
+ currentValue = v
+ publish()
+ }
+ }
+
+ class Display extends Observer {
+ def notify(sub: Sensor) =
+ println(s"${sub.label} has value ${sub.value}")
+ }
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+object SensorReader extends SubjectObserver:
+ type S = Sensor
+ type O = Display
+
+ class Sensor(val label: String) extends Subject:
+ private var currentValue = 0.0
+ def value = currentValue
+ def changeValue(v: Double) =
+ currentValue = v
+ publish()
+
+ class Display extends Observer:
+ def notify(sub: Sensor) =
+ println(s"${sub.label} has value ${sub.value}")
+```
+
+{% endtab %}
+{% endtabs %}
+
+В частности, мы определяем _singleton_ `object SensorReader`, который расширяет `SubjectObserver`.
+В реализации `SensorReader` говорится, что тип `S` теперь определяется как тип `Sensor`,
+а тип `O` определяется как тип `Display`.
+И `Sensor`, и `Display` определяются как вложенные классы в `SensorReader`,
+реализующие trait-ы `Subject` и `Observer` соответственно.
+
+Помимо того, что этот код является примером сервис-ориентированного дизайна,
+он также освещает многие аспекты объектно-ориентированного программирования:
+
+- Класс `Sensor` вводит свое собственное частное состояние (`currentValue`)
+ и инкапсулирует изменение состояния за методом `changeValue`.
+- Реализация `changeValue` использует метод `publish`, определенный в родительском trait-е.
+- Класс `Display` расширяет trait `Observer` и реализует отсутствующий метод `notify`.
+
+Важно отметить, что реализация `notify` может безопасно получить доступ только к `label` и значению `sub`,
+поскольку мы изначально объявили параметр типа `S`.
+
+### Использование компонента
+
+Наконец, следующий код иллюстрирует, как использовать компонент `SensorReader`:
+
+{% tabs example_3 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+import SensorReader._
+
+// настройка сети
+val s1 = new Sensor("sensor1")
+val s2 = new Sensor("sensor2")
+val d1 = new Display()
+val d2 = new Display()
+s1.subscribe(d1)
+s1.subscribe(d2)
+s2.subscribe(d1)
+
+// распространение обновлений по сети
+s1.changeValue(2)
+s2.changeValue(3)
+
+// печатает:
+// sensor1 has value 2.0
+// sensor1 has value 2.0
+// sensor2 has value 3.0
+
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+import SensorReader.*
+
+// настройка сети
+val s1 = Sensor("sensor1")
+val s2 = Sensor("sensor2")
+val d1 = Display()
+val d2 = Display()
+s1.subscribe(d1)
+s1.subscribe(d2)
+s2.subscribe(d1)
+
+// распространение обновлений по сети
+s1.changeValue(2)
+s2.changeValue(3)
+
+// печатает:
+// sensor1 has value 2.0
+// sensor1 has value 2.0
+// sensor2 has value 3.0
+```
+
+{% endtab %}
+{% endtabs %}
+
+Имея под рукой все утилиты объектно-ориентированного программирования, в следующем разделе будет продемонстрировано,
+как разрабатывать программы в функциональном стиле.
+
+[scalable]: https://doi.org/10.1145/1094811.1094815
+[open]: {{ site.scala3ref }}/other-new-features/open-classes.html
+[trait-params]: {{ site.scala3ref }}/other-new-features/trait-parameters.html
diff --git a/_ru/scala3/book/domain-modeling-tools.md b/_ru/scala3/book/domain-modeling-tools.md
new file mode 100644
index 0000000000..36f19bc3ad
--- /dev/null
+++ b/_ru/scala3/book/domain-modeling-tools.md
@@ -0,0 +1,1175 @@
+---
+layout: multipage-overview
+title: Инструменты
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: section
+description: В этой главе представлено введение в доступные инструменты моделирования предметной области в Scala 3, включая классы, трейты, перечисления и многое другое.
+language: ru
+num: 20
+previous-page: domain-modeling-intro
+next-page: domain-modeling-oop
+---
+
+
+Scala предоставляет множество различных конструкций для моделирования предметной области:
+
+- Классы
+- Объекты
+- Сопутствующие объекты
+- Трейты
+- Абстрактные классы
+- Перечисления только в Scala 3
+- Case классы
+- Case объекты
+
+В этом разделе кратко представлена каждая из этих языковых конструкций.
+
+
+## Классы
+
+Как и в других языках, _класс_ в Scala — это шаблон для создания экземпляров объекта.
+Вот несколько примеров классов:
+
+{% tabs class_1 %}
+{% tab 'Scala 2 и 3' %}
+
+```scala
+class Person(var name: String, var vocation: String)
+class Book(var title: String, var author: String, var year: Int)
+class Movie(var name: String, var director: String, var year: Int)
+```
+
+{% endtab %}
+{% endtabs %}
+
+Эти примеры показывают, что в Scala есть очень легкий способ объявления классов.
+
+Все параметры в примерах наших классов определены как `var` поля, а значит, они изменяемы: их можно читать, а также изменять.
+Если вы хотите, чтобы они были неизменяемыми — только для чтения — создайте их как `val` поля или используйте case класс.
+
+До Scala 3 для создания нового экземпляра класса использовалось ключевое слово `new`:
+
+{% tabs class_2 %}
+{% tab 'Scala 2 и 3' %}
+
+```scala
+val p = new Person("Robert Allen Zimmerman", "Harmonica Player")
+// ---
+```
+
+{% endtab %}
+{% endtabs %}
+
+Однако с [универсальными apply методами][creator] в Scala 3 этого больше не требуется: только в Scala 3.
+
+{% tabs class_3 %}
+{% tab 'Только в Scala 3' %}
+
+```scala
+val p = Person("Robert Allen Zimmerman", "Harmonica Player")
+```
+
+{% endtab %}
+{% endtabs %}
+
+Если у вас есть экземпляр класса, такой как `p`, то вы можете получить доступ к полям экземпляра,
+которые в этом примере являются параметрами конструктора:
+
+{% tabs class_4 %}
+{% tab 'Scala 2 и 3' %}
+
+```scala
+p.name // "Robert Allen Zimmerman"
+p.vocation // "Harmonica Player"
+```
+
+{% endtab %}
+{% endtabs %}
+
+Как уже упоминалось, все эти параметры были созданы как `var` поля, поэтому они изменяемые:
+
+{% tabs class_5 %}
+{% tab 'Scala 2 и 3' %}
+
+```scala
+p.name = "Bob Dylan"
+p.vocation = "Musician"
+```
+
+{% endtab %}
+{% endtabs %}
+
+### Поля и методы
+
+Классы также могут содержать методы и дополнительные поля, не являющиеся частью конструкторов.
+Они определены в теле класса.
+Тело инициализируется как часть конструктора по умолчанию:
+
+{% tabs method class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+class Person(var firstName: String, var lastName: String) {
+
+ println("initialization begins")
+ val fullName = firstName + " " + lastName
+
+ // метод класса
+ def printFullName: Unit =
+ // обращение к полю `fullName`, определенному выше
+ println(fullName)
+
+ printFullName
+ println("initialization ends")
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+class Person(var firstName: String, var lastName: String):
+
+ println("initialization begins")
+ val fullName = firstName + " " + lastName
+
+ // метод класса
+ def printFullName: Unit =
+ // обращение к полю `fullName`, определенному выше
+ println(fullName)
+
+ printFullName
+ println("initialization ends")
+```
+
+{% endtab %}
+{% endtabs %}
+
+Следующая сессия REPL показывает, как создать новый экземпляр `Person` с этим классом:
+
+{% tabs demo-person class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+````scala
+scala> val john = new Person("John", "Doe")
+initialization begins
+John Doe
+initialization ends
+val john: Person = Person@55d8f6bb
+
+scala> john.printFullName
+John Doe
+````
+{% endtab %}
+{% tab 'Scala 3' %}
+````scala
+scala> val john = Person("John", "Doe")
+initialization begins
+John Doe
+initialization ends
+val john: Person = Person@55d8f6bb
+
+scala> john.printFullName
+John Doe
+````
+{% endtab %}
+{% endtabs %}
+
+Классы также могут расширять трейты и абстрактные классы, которые мы рассмотрим в специальных разделах ниже.
+
+### Значения параметров по умолчанию
+
+В качестве беглого взгляда на некоторые другие функции,
+параметры конструктора класса также могут иметь значения по умолчанию:
+
+{% tabs default-values_1 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+class Socket(val timeout: Int = 5_000, val linger: Int = 5_000) {
+ override def toString = s"timeout: $timeout, linger: $linger"
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+class Socket(val timeout: Int = 5_000, val linger: Int = 5_000):
+ override def toString = s"timeout: $timeout, linger: $linger"
+```
+
+{% endtab %}
+{% endtabs %}
+
+Отличительной особенностью этой функции является то, что она позволяет потребителям вашего кода
+создавать классы различными способами, как если бы у класса были альтернативные конструкторы:
+
+{% tabs default-values_2 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+val s = new Socket() // timeout: 5000, linger: 5000
+val s = new Socket(2_500) // timeout: 2500, linger: 5000
+val s = new Socket(10_000, 10_000) // timeout: 10000, linger: 10000
+val s = new Socket(timeout = 10_000) // timeout: 10000, linger: 5000
+val s = new Socket(linger = 10_000) // timeout: 5000, linger: 10000
+```
+
+{% endtab %}
+{% tab 'Scala 3' %}
+
+```scala
+val s = Socket() // timeout: 5000, linger: 5000
+val s = Socket(2_500) // timeout: 2500, linger: 5000
+val s = Socket(10_000, 10_000) // timeout: 10000, linger: 10000
+val s = Socket(timeout = 10_000) // timeout: 10000, linger: 5000
+val s = Socket(linger = 10_000) // timeout: 5000, linger: 10000
+```
+
+{% endtab %}
+{% endtabs %}
+
+При создании нового экземпляра класса вы также можете использовать именованные параметры.
+Это особенно полезно, когда несколько параметров имеют одинаковый тип, как показано в этом сравнении:
+
+{% tabs default-values_3 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+// пример 1
+val s = new Socket(10_000, 10_000)
+
+// пример 2
+val s = new Socket(
+ timeout = 10_000,
+ linger = 10_000
+)
+```
+
+{% endtab %}
+{% tab 'Scala 3' %}
+
+```scala
+// пример 1
+val s = Socket(10_000, 10_000)
+
+// пример 2
+val s = Socket(
+ timeout = 10_000,
+ linger = 10_000
+)
+```
+
+{% endtab %}
+{% endtabs %}
+
+### Вспомогательные конструкторы
+
+Вы можете определить класс с несколькими конструкторами,
+чтобы клиенты вашего класса могли создавать его различными способами.
+Например, предположим, что вам нужно написать код для моделирования студентов в системе приема в колледж.
+При анализе требований вы увидели, что необходимо создавать экземпляр `Student` тремя способами:
+
+- С именем и государственным удостоверением личности, когда они впервые начинают процесс приема
+- С именем, государственным удостоверением личности и дополнительной датой подачи заявки, когда они подают заявку
+- С именем, государственным удостоверением личности и студенческим билетом после того, как они будут приняты
+
+Один из способов справиться с этой ситуацией в стиле ООП - с помощью нижеследующего кода:
+
+{% tabs structor_1 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+import java.time._
+
+// [1] основной конструктор
+class Student(
+ var name: String,
+ var govtId: String
+) {
+ private var _applicationDate: Option[LocalDate] = None
+ private var _studentId: Int = 0
+
+ // [2] конструктор для студента, подавшего заявку
+ def this(
+ name: String,
+ govtId: String,
+ applicationDate: LocalDate
+ ) = {
+ this(name, govtId)
+ _applicationDate = Some(applicationDate)
+ }
+
+ // [3] конструктор, когда учащийся принят и теперь имеет студенческий билет
+ def this(
+ name: String,
+ govtId: String,
+ studentId: Int
+ ) = {
+ this(name, govtId)
+ _studentId = studentId
+ }
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+import java.time.*
+
+// [1] основной конструктор
+class Student(
+ var name: String,
+ var govtId: String
+):
+ private var _applicationDate: Option[LocalDate] = None
+ private var _studentId: Int = 0
+
+ // [2] конструктор для студента, подавшего заявку
+ def this(
+ name: String,
+ govtId: String,
+ applicationDate: LocalDate
+ ) =
+ this(name, govtId)
+ _applicationDate = Some(applicationDate)
+
+ // [3] конструктор, когда учащийся принят и теперь имеет студенческий билет
+ def this(
+ name: String,
+ govtId: String,
+ studentId: Int
+ ) =
+ this(name, govtId)
+ _studentId = studentId
+```
+
+{% endtab %}
+{% endtabs %}
+
+Класс содержит три конструктора, обозначенных комментариями в коде:
+
+1. Первичный конструктор, заданный `name` и `govtId` в определении класса
+2. Вспомогательный конструктор с параметрами `name`, `govtId` и `applicationDate`
+3. Другой вспомогательный конструктор с параметрами `name`, `govtId` и `studentId`
+
+Эти конструкторы можно вызывать следующим образом:
+
+{% tabs structor_2 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+val s1 = new Student("Mary", "123")
+val s2 = new Student("Mary", "123", LocalDate.now)
+val s3 = new Student("Mary", "123", 456)
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+val s1 = Student("Mary", "123")
+val s2 = Student("Mary", "123", LocalDate.now)
+val s3 = Student("Mary", "123", 456)
+```
+
+{% endtab %}
+{% endtabs %}
+
+Хотя этот метод можно использовать, имейте в виду, что параметры конструктора также могут иметь значения по умолчанию,
+из-за чего создается впечатление, что класс содержит несколько конструкторов.
+Это показано в предыдущем примере `Socket`.
+
+## Объекты
+
+Объект — это класс, который имеет ровно один экземпляр.
+Инициализируется он лениво, тогда, когда на его элементы ссылаются, подобно `lazy val`.
+Объекты в Scala позволяют группировать методы и поля в одном пространстве имен, аналогично тому,
+как вы используете `static` члены в классе в Java, Javascript (ES6) или `@staticmethod` в Python.
+
+Объявление `object` аналогично объявлению `class`.
+Вот пример объекта “строковые утилиты”, который содержит набор методов для работы со строками:
+
+{% tabs object_1 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+object StringUtils {
+ def truncate(s: String, length: Int): String = s.take(length)
+ def containsWhitespace(s: String): Boolean = s.object_1es(".*\\s.*")
+ def isNullOrEmpty(s: String): Boolean = s == null || s.trim.isEmpty
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+object StringUtils:
+ def truncate(s: String, length: Int): String = s.take(length)
+ def containsWhitespace(s: String): Boolean = s.object_1es(".*\\s.*")
+ def isNullOrEmpty(s: String): Boolean = s == null || s.trim.isEmpty
+```
+
+{% endtab %}
+{% endtabs %}
+
+Мы можем использовать объект следующим образом:
+
+{% tabs object_2 %}
+{% tab 'Scala 2 и 3' %}
+
+```scala
+StringUtils.truncate("Chuck Bartowski", 5) // "Chuck"
+```
+
+{% endtab %}
+{% endtabs %}
+
+Импорт в Scala очень гибкий и позволяет импортировать _все_ члены объекта:
+
+{% tabs object_3 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+import StringUtils._
+truncate("Chuck Bartowski", 5) // "Chuck"
+containsWhitespace("Sarah Walker") // true
+isNullOrEmpty("John Casey") // false
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+import StringUtils.*
+truncate("Chuck Bartowski", 5) // "Chuck"
+containsWhitespace("Sarah Walker") // true
+isNullOrEmpty("John Casey") // false
+```
+
+{% endtab %}
+{% endtabs %}
+
+или только _некоторые_:
+
+{% tabs object_4 %}
+{% tab 'Scala 2 и 3' %}
+
+```scala
+import StringUtils.{truncate, containsWhitespace}
+truncate("Charles Carmichael", 7) // "Charles"
+containsWhitespace("Captain Awesome") // true
+isNullOrEmpty("Morgan Grimes") // Not found: isNullOrEmpty (error)
+```
+
+{% endtab %}
+{% endtabs %}
+
+Объекты также могут содержать поля, доступ к которым также осуществляется как к статическим элементам:
+
+{% tabs object_5 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+object MathConstants {
+ val PI = 3.14159
+ val E = 2.71828
+}
+
+println(MathConstants.PI) // 3.14159
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+object MathConstants:
+ val PI = 3.14159
+ val E = 2.71828
+
+println(MathConstants.PI) // 3.14159
+```
+
+{% endtab %}
+{% endtabs %}
+
+## Сопутствующие объекты
+
+Объект `object`, имеющий то же имя, что и класс, и объявленный в том же файле, что и класс,
+называется _"сопутствующим объектом"_. Точно так же соответствующий класс называется сопутствующим классом объекта.
+Сопутствующие класс или объект могут получить доступ к закрытым членам своего “соседа”.
+
+Сопутствующие объекты используются для методов и значений, не относящихся к экземплярам сопутствующего класса.
+Например, в следующем примере у класса `Circle` есть элемент с именем `area`, специфичный для каждого экземпляра,
+а у его сопутствующего объекта есть метод с именем `calculateArea`,
+который (а) не специфичен для экземпляра и (б) доступен для каждого экземпляра:
+
+{% tabs companion class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+import scala.math._
+
+class Circle(val radius: Double) {
+ def area: Double = Circle.calculateArea(radius)
+}
+
+object Circle {
+ private def calculateArea(radius: Double): Double = Pi * pow(radius, 2.0)
+}
+
+val circle1 = new Circle(5.0)
+circle1.area
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+import scala.math.*
+
+class Circle(val radius: Double):
+ def area: Double = Circle.calculateArea(radius)
+
+object Circle:
+ private def calculateArea(radius: Double): Double = Pi * pow(radius, 2.0)
+
+val circle1 = Circle(5.0)
+circle1.area
+```
+
+{% endtab %}
+{% endtabs %}
+
+В этом примере метод `area`, доступный для каждого экземпляра `Circle`,
+использует метод `calculateArea`, определенный в сопутствующем объекте.
+Кроме того, поскольку `calculateArea` является приватным, к нему нельзя получить доступ с помощью другого кода,
+но, как показано, его могут видеть экземпляры класса `Circle`.
+
+### Другие виды использования сопутствующих объектов
+
+Сопутствующие объекты могут использоваться для нескольких целей:
+
+- их можно использовать для группировки “статических” методов в пространстве имен, как в примере выше
+ - эти методы могут быть `public` или `private`
+ - если бы `calculateArea` был `public`, к нему можно было бы получить доступ из любого места как `Circle.calculateArea`
+- они могут содержать методы `apply`, которые — благодаря некоторому синтаксическому сахару —
+ работают как фабричные методы для создания новых экземпляров
+- они могут содержать методы `unapply`, которые используются для деконструкции объектов, например, с помощью сопоставления с шаблоном
+
+Вот краткий обзор того, как методы `apply` можно использовать в качестве фабричных методов для создания новых объектов:
+
+{% tabs companion-use class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+class Person {
+ var name = ""
+ var age = 0
+ override def toString = s"$name is $age years old"
+}
+
+object Person {
+ // фабричный метод с одним аргументом
+ def apply(name: String): Person = {
+ var p = new Person
+ p.name = name
+ p
+ }
+
+ // фабричный метод с двумя аргументами
+ def apply(name: String, age: Int): Person = {
+ var p = new Person
+ p.name = name
+ p.age = age
+ p
+ }
+}
+
+val joe = Person("Joe")
+val fred = Person("Fred", 29)
+
+//val joe: Person = Joe is 0 years old
+//val fred: Person = Fred is 29 years old
+```
+
+Метод `unapply` здесь не рассматривается, но описан в [Спецификации языка](https://scala-lang.org/files/archive/spec/2.13/08-pattern-matching.html#extractor-patterns).
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+class Person:
+ var name = ""
+ var age = 0
+ override def toString = s"$name is $age years old"
+
+object Person:
+
+ // фабричный метод с одним аргументом
+ def apply(name: String): Person =
+ var p = new Person
+ p.name = name
+ p
+
+ // фабричный метод с двумя аргументами
+ def apply(name: String, age: Int): Person =
+ var p = new Person
+ p.name = name
+ p.age = age
+ p
+
+end Person
+
+val joe = Person("Joe")
+val fred = Person("Fred", 29)
+
+//val joe: Person = Joe is 0 years old
+//val fred: Person = Fred is 29 years old
+```
+
+Метод `unapply` здесь не рассматривается, но описан в [справочной документации]({{ site.scala3ref }}/changed-features/pattern-matching.html).
+
+{% endtab %}
+{% endtabs %}
+
+## Трейты
+
+Если провести аналогию с Java, то Scala `trait` похож на интерфейс в Java 8+.
+Trait-ы могут содержать:
+
+- абстрактные методы и поля
+- конкретные методы и поля
+
+В базовом использовании `trait` может использоваться как интерфейс, определяющий только абстрактные члены,
+которые будут реализованы другими классами:
+
+{% tabs traits_1 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+trait Employee {
+ def id: Int
+ def firstName: String
+ def lastName: String
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+trait Employee:
+ def id: Int
+ def firstName: String
+ def lastName: String
+```
+
+{% endtab %}
+{% endtabs %}
+
+Однако трейты также могут содержать конкретные члены.
+Например, следующий трейт определяет два абстрактных члена — `numLegs` и `walk()` —
+а также имеет конкретную реализацию метода `stop()`:
+
+{% tabs traits_2 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+trait HasLegs {
+ def numLegs: Int
+ def walk(): Unit
+ def stop() = println("Stopped walking")
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+trait HasLegs:
+ def numLegs: Int
+ def walk(): Unit
+ def stop() = println("Stopped walking")
+```
+
+{% endtab %}
+{% endtabs %}
+
+Вот еще один трейт с абстрактным членом и двумя конкретными реализациями:
+
+{% tabs traits_3 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+trait HasTail {
+ def tailColor: String
+ def wagTail() = println("Tail is wagging")
+ def stopTail() = println("Tail is stopped")
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+trait HasTail:
+ def tailColor: String
+ def wagTail() = println("Tail is wagging")
+ def stopTail() = println("Tail is stopped")
+```
+
+{% endtab %}
+{% endtabs %}
+
+Обратите внимание, что каждый трейт обрабатывает только очень специфичные атрибуты и поведение:
+`HasLegs` имеет дело только с "лапами", а `HasTail` имеет дело только с функциональностью, связанной с хвостом.
+Трейты позволяют создавать такие небольшие модули.
+
+Позже в вашем коде классы могут смешивать несколько трейтов для создания более крупных компонентов:
+
+{% tabs traits_4 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+class IrishSetter(name: String) extends HasLegs with HasTail {
+ val numLegs = 4
+ val tailColor = "Red"
+ def walk() = println("I’m walking")
+ override def toString = s"$name is a Dog"
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+class IrishSetter(name: String) extends HasLegs, HasTail:
+ val numLegs = 4
+ val tailColor = "Red"
+ def walk() = println("I’m walking")
+ override def toString = s"$name is a Dog"
+```
+
+{% endtab %}
+{% endtabs %}
+
+Обратите внимание, что класс `IrishSetter` реализует абстрактные члены, определенные в `HasLegs` и `HasTail`.
+Теперь вы можете создавать новые экземпляры `IrishSetter`:
+
+{% tabs traits_5 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+val d = new IrishSetter("Big Red") // "Big Red is a Dog"
+```
+
+{% endtab %}
+{% tab 'Scala 3' %}
+
+```scala
+val d = IrishSetter("Big Red") // "Big Red is a Dog"
+```
+
+{% endtab %}
+{% endtabs %}
+
+Это всего лишь пример того, чего можно добиться с помощью trait-ов.
+Дополнительные сведения см. в остальных уроках по моделированию.
+
+## Абстрактные классы
+
+Когда необходимо написать класс, но известно, что в нем будут абстрактные члены, можно создать либо `trait`, либо абстрактный класс.
+В большинстве случаев желательно использовать `trait`, но исторически сложилось так, что было две ситуации,
+когда предпочтительнее использование абстрактного класса:
+
+- необходимо создать базовый класс, который принимает аргументы конструктора
+- код будет вызван из Java-кода
+
+### Базовый класс, который принимает аргументы конструктора
+
+До Scala 3, когда базовому классу нужно было принимать аргументы конструктора, он объявлялся как `abstract class`:
+
+{% tabs abstract_1 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+abstract class Pet(name: String) {
+ def greeting: String
+ def age: Int
+ override def toString = s"My name is $name, I say $greeting, and I’m $age"
+}
+
+class Dog(name: String, var age: Int) extends Pet(name) {
+ val greeting = "Woof"
+}
+
+val d = new Dog("Fido", 1)
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+abstract class Pet(name: String):
+ def greeting: String
+ def age: Int
+ override def toString = s"My name is $name, I say $greeting, and I’m $age"
+
+class Dog(name: String, var age: Int) extends Pet(name):
+ val greeting = "Woof"
+
+val d = Dog("Fido", 1)
+```
+
+{% endtab %}
+{% endtabs %}
+
+
Параметры в trait только в Scala 3
+
+Однако в Scala 3 трейты теперь могут иметь [параметры][trait-params],
+так что теперь вы можете использовать трейты в той же ситуации:
+
+{% tabs abstract_2 %}
+
+{% tab 'Только в Scala 3' %}
+
+```scala
+trait Pet(name: String):
+ def greeting: String
+ def age: Int
+ override def toString = s"My name is $name, I say $greeting, and I’m $age"
+
+class Dog(name: String, var age: Int) extends Pet(name):
+ val greeting = "Woof"
+
+val d = Dog("Fido", 1)
+```
+
+{% endtab %}
+{% endtabs %}
+
+Trait-ы более гибки в составлении, потому что можно смешивать (наследовать) несколько trait-ов, но только один класс.
+В большинстве случаев trait-ы следует предпочитать классам и абстрактным классам.
+Правило выбора состоит в том, чтобы использовать классы всякий раз, когда необходимо создавать экземпляры определенного типа,
+и trait-ы, когда желательно разложить и повторно использовать поведение.
+
+
Перечисления только в Scala 3
+
+Перечисление (_an enumeration_) может быть использовано для определения типа,
+состоящего из конечного набора именованных значений (в разделе, посвященном [моделированию ФП][fp-modeling],
+будут показаны дополнительные возможности перечислений).
+Базовые перечисления используются для определения наборов констант,
+таких как месяцы в году, дни в неделе, направления, такие как север/юг/восток/запад, и многое другое.
+
+В качестве примера, рассмотрим перечисления, определяющие наборы атрибутов, связанных с пиццами:
+
+{% tabs enum_1 %}
+{% tab 'Только в Scala 3' %}
+
+```scala
+enum CrustSize:
+ case Small, Medium, Large
+
+enum CrustType:
+ case Thin, Thick, Regular
+
+enum Topping:
+ case Cheese, Pepperoni, BlackOlives, GreenOlives, Onions
+```
+
+{% endtab %}
+{% endtabs %}
+
+Для использования в коде в первую очередь перечисление нужно импортировать, а затем - использовать:
+
+{% tabs enum_2 %}
+{% tab 'Только в Scala 3' %}
+
+```scala
+import CrustSize.*
+val currentCrustSize = Small
+```
+
+{% endtab %}
+{% endtabs %}
+
+Значения перечислений можно сравнивать (`==`) и использовать в сопоставлении:
+
+{% tabs enum_3 %}
+{% tab 'Только в Scala 3' %}
+
+```scala
+// if/then
+if currentCrustSize == Large then
+ println("You get a prize!")
+
+// match
+currentCrustSize match
+ case Small => println("small")
+ case Medium => println("medium")
+ case Large => println("large")
+```
+
+{% endtab %}
+{% endtabs %}
+
+### Дополнительные функции перечисления
+
+Перечисления также могут быть параметризованы:
+
+{% tabs enum_4 %}
+{% tab 'Только в Scala 3' %}
+
+```scala
+enum Color(val rgb: Int):
+ case Red extends Color(0xFF0000)
+ case Green extends Color(0x00FF00)
+ case Blue extends Color(0x0000FF)
+```
+
+{% endtab %}
+{% endtabs %}
+
+И они также могут содержать элементы (например, поля и методы):
+
+{% tabs enum_5 %}
+{% tab 'Только в Scala 3' %}
+
+```scala
+enum Planet(mass: Double, radius: Double):
+ private final val G = 6.67300E-11
+ def surfaceGravity = G * mass / (radius * radius)
+ def surfaceWeight(otherMass: Double) =
+ otherMass * surfaceGravity
+
+ case Mercury extends Planet(3.303e+23, 2.4397e6)
+ case Earth extends Planet(5.976e+24, 6.37814e6)
+ // далее идут остальные планеты ...
+```
+
+{% endtab %}
+{% endtabs %}
+
+### Совместимость с перечислениями Java
+
+Если вы хотите использовать перечисления, определенные в Scala, как перечисления Java,
+то можете сделать это, расширив класс `java.lang.Enum` (импортированный по умолчанию) следующим образом:
+
+{% tabs enum_6 %}
+{% tab 'Только в Scala 3' %}
+
+```scala
+enum Color extends Enum[Color] { case Red, Green, Blue }
+```
+
+{% endtab %}
+{% endtabs %}
+
+Параметр типа берется из определения Java `enum` и должен совпадать с типом перечисления.
+Нет необходимости предоставлять аргументы конструктора (как определено в документации Java API) для `java.lang.Enum`
+при его расширении — компилятор генерирует их автоматически.
+
+После такого определения `Color` вы можете использовать его так же, как перечисление Java:
+
+````
+scala> Color.Red.compareTo(Color.Green)
+val res0: Int = -1
+````
+
+В разделе об [алгебраических типах данных][adts] и [справочной документации][ref-enums] перечисления рассматриваются более подробно.
+
+## Case class-ы
+
+Case class используются для моделирования неизменяемых структур данных.
+Возьмем следующий пример:
+
+{% tabs case-classes_1 %}
+{% tab 'Scala 2 и 3' %}
+
+```scala:
+case class Person(name: String, relation: String)
+```
+
+{% endtab %}
+{% endtabs %}
+
+Поскольку мы объявляем `Person` как `case class`, поля `name` и `relation` по умолчанию общедоступны и неизменяемы.
+Мы можем создавать экземпляры case классов следующим образом:
+
+{% tabs case-classes_2 %}
+{% tab 'Scala 2 и 3' %}
+
+```scala
+val christina = Person("Christina", "niece")
+```
+
+{% endtab %}
+{% endtabs %}
+
+Обратите внимание, что поля не могут быть изменены:
+
+{% tabs case-classes_3 %}
+{% tab 'Scala 2 и 3' %}
+
+```scala
+christina.name = "Fred" // ошибка: reassignment to val
+```
+
+{% endtab %}
+{% endtabs %}
+
+Поскольку предполагается, что поля case класса неизменяемы,
+компилятор Scala может сгенерировать для вас множество полезных методов:
+
+- Генерируется метод `unapply`, позволяющий выполнять сопоставление с образцом case класса (то есть `case Person(n, r) => ...`).
+- В классе генерируется метод `copy`, полезный для создания модифицированных копий экземпляра.
+- Генерируются методы `equals` и `hashCode`, использующие структурное равенство,
+ что позволяет использовать экземпляры case классов в `Map`-ах.
+- Генерируется дефолтный метод `toString`, полезный для отладки.
+
+Эти дополнительные функции показаны в следующем примере:
+
+{% tabs case-classes_4 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+// Case class-ы можно использовать в качестве шаблонов
+christina match {
+ case Person(n, r) => println("name is " + n)
+}
+
+// для вас генерируются методы `equals` и `hashCode`
+val hannah = Person("Hannah", "niece")
+christina == hannah // false
+
+// метод `toString`
+println(christina) // Person(Christina,niece)
+
+// встроенный метод `copy`
+case class BaseballTeam(name: String, lastWorldSeriesWin: Int)
+val cubs1908 = BaseballTeam("Chicago Cubs", 1908)
+val cubs2016 = cubs1908.copy(lastWorldSeriesWin = 2016)
+// в результате:
+// cubs2016: BaseballTeam = BaseballTeam(Chicago Cubs,2016)
+
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+// Case class-ы можно использовать в качестве шаблонов
+christina match
+ case Person(n, r) => println("name is " + n)
+
+// для вас генерируются методы `equals` и `hashCode`
+val hannah = Person("Hannah", "niece")
+christina == hannah // false
+
+// метод `toString`
+println(christina) // Person(Christina,niece)
+
+// встроенный метод `copy`
+case class BaseballTeam(name: String, lastWorldSeriesWin: Int)
+val cubs1908 = BaseballTeam("Chicago Cubs", 1908)
+val cubs2016 = cubs1908.copy(lastWorldSeriesWin = 2016)
+// в результате:
+// cubs2016: BaseballTeam = BaseballTeam(Chicago Cubs,2016)
+```
+
+{% endtab %}
+{% endtabs %}
+
+### Поддержка функционального программирования
+
+Как уже упоминалось ранее, case class-ы поддерживают функциональное программирование (ФП):
+
+- ФП избегает изменения структур данных.
+ Поэтому поля конструктора по умолчанию имеют значение `val`.
+ Поскольку экземпляры case class не могут быть изменены, ими можно легко делиться, не опасаясь мутаций или условий гонки.
+- вместо изменения экземпляра можно использовать метод `copy` в качестве шаблона для создания нового (потенциально измененного) экземпляра.
+ Этот процесс можно назвать “обновлением по мере копирования”.
+- наличие автоматически сгенерированного метода `unapply` позволяет использовать case class в сопоставлении шаблонов.
+
+## Case object-ы
+
+Case object-ы относятся к объектам так же, как case class-ы относятся к классам:
+они предоставляют ряд автоматически генерируемых методов, чтобы сделать их более мощными.
+Case object-ы особенно полезны тогда, когда необходим одноэлементный объект,
+который нуждается в небольшой дополнительной функциональности,
+например, для использования с сопоставлением шаблонов в выражениях `match`.
+
+Case object-ы полезны, когда необходимо передавать неизменяемые сообщения.
+Например, представим проект музыкального проигрывателя, и создадим набор команд или сообщений:
+
+{% tabs case-objects_1 %}
+{% tab 'Scala 2 и 3' %}
+
+```scala
+sealed trait Message
+case class PlaySong(name: String) extends Message
+case class IncreaseVolume(amount: Int) extends Message
+case class DecreaseVolume(amount: Int) extends Message
+case object StopPlaying extends Message
+```
+
+{% endtab %}
+{% endtabs %}
+
+Затем в других частях кода можно написать методы, которые используют сопоставление с образцом
+для обработки входящего сообщения
+(при условии, что методы `playSong`, `changeVolume` и `stopPlayingSong` определены где-то еще):
+
+{% tabs case-objects_2 class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+def handleMessages(message: Message): Unit = message match {
+ case PlaySong(name) => playSong(name)
+ case IncreaseVolume(amount) => changeVolume(amount)
+ case DecreaseVolume(amount) => changeVolume(-amount)
+ case StopPlaying => stopPlayingSong()
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+def handleMessages(message: Message): Unit = message match
+ case PlaySong(name) => playSong(name)
+ case IncreaseVolume(amount) => changeVolume(amount)
+ case DecreaseVolume(amount) => changeVolume(-amount)
+ case StopPlaying => stopPlayingSong()
+```
+
+{% endtab %}
+{% endtabs %}
+
+[ref-enums]: {{ site.scala3ref }}/enums/enums.html
+[adts]: {% link _overviews/scala3-book/types-adts-gadts.md %}
+[fp-modeling]: {% link _overviews/scala3-book/domain-modeling-fp.md %}
+[creator]: {{ site.scala3ref }}/other-new-features/creator-applications.html
+[unapply]: {{ site.scala3ref }}/changed-features/pattern-matching.html
+[trait-params]: {{ site.scala3ref }}/other-new-features/trait-parameters.html
diff --git a/_ru/scala3/book/first-look-at-types.md b/_ru/scala3/book/first-look-at-types.md
index 603157c6d1..c1bbe20d4b 100644
--- a/_ru/scala3/book/first-look-at-types.md
+++ b/_ru/scala3/book/first-look-at-types.md
@@ -9,7 +9,7 @@ description: На этой странице представлено кратк
language: ru
num: 17
previous-page: taste-summary
-next-page:
+next-page: control-structures
---
@@ -47,7 +47,7 @@ next-page:
такие как следующий метод, для той же цели используется `Unit`:
{% tabs unit %}
-{% tab 'Scala 2 and 3' for=unit %}
+{% tab 'Scala 2 и 3' for=unit %}
```scala
def printIt(a: Any): Unit = println(a)
```
@@ -58,7 +58,7 @@ def printIt(a: Any): Unit = println(a)
и могут обрабатываться так же, как и любой другой объект:
{% tabs any %}
-{% tab 'Scala 2 and 3' for=any %}
+{% tab 'Scala 2 и 3' for=any %}
```scala
val list: List[Any] = List(
"a string",
@@ -93,7 +93,7 @@ true
В этих примерах показано, как объявлять переменные этих числовых типов:
{% tabs anyval %}
-{% tab 'Scala 2 and 3' for=anyval %}
+{% tab 'Scala 2 и 3' for=anyval %}
```scala
val b: Byte = 1
val i: Int = 1
@@ -113,7 +113,7 @@ val f: Float = 3.0
Поскольку `Int` и `Double` являются числовыми типами по умолчанию, их можно создавать без явного объявления типа данных:
{% tabs anynum %}
-{% tab 'Scala 2 and 3' for=anynum %}
+{% tab 'Scala 2 и 3' for=anynum %}
```scala
val i = 123 // по умолчанию Int
val x = 1.0 // по умолчанию Double
@@ -125,7 +125,7 @@ val x = 1.0 // по умолчанию Double
для того, чтобы задать `Long`, `Double` или `Float` значения:
{% tabs type-post %}
-{% tab 'Scala 2 and 3' for=type-post %}
+{% tab 'Scala 2 и 3' for=type-post %}
```scala
val x = 1_000L // val x: Long = 1000
val y = 2.2D // val y: Double = 2.2
@@ -137,7 +137,7 @@ val z = 3.3F // val z: Float = 3.3
В Scala также есть типы `String` и `Char`, которые обычно можно объявить в неявной форме:
{% tabs type-string %}
-{% tab 'Scala 2 and 3' for=type-string %}
+{% tab 'Scala 2 и 3' for=type-string %}
```scala
val s = "Bill"
val c = 'a'
@@ -168,7 +168,7 @@ val c = 'a'
Для действительно больших чисел можно использовать типы `BigInt` и `BigDecimal`:
{% tabs type-bigint %}
-{% tab 'Scala 2 and 3' for=type-bigint %}
+{% tab 'Scala 2 и 3' for=type-bigint %}
```scala
val a = BigInt(1_234_567_890_987_654_321L)
val b = BigDecimal(123_456.789)
@@ -182,7 +182,7 @@ val b = BigDecimal(123_456.789)
`BigInt` и `BigDecimal` поддерживают все привычные числовые операторы:
{% tabs type-bigint2 %}
-{% tab 'Scala 2 and 3' for=type-bigint2 %}
+{% tab 'Scala 2 и 3' for=type-bigint2 %}
```scala
val b = BigInt(1234567890) // scala.math.BigInt = 1234567890
val c = b + b // scala.math.BigInt = 2469135780
@@ -204,7 +204,7 @@ val d = b * b // scala.math.BigInt = 1524157875019052100
Например, учитывая эти три переменные:
{% tabs string-inside1 %}
-{% tab 'Scala 2 and 3' for=string-inside1 %}
+{% tab 'Scala 2 и 3' for=string-inside1 %}
```scala
val firstName = "John"
val mi = 'C'
@@ -216,7 +216,7 @@ val lastName = "Doe"
их комбинацию можно получить так:
{% tabs string-inside2 %}
-{% tab 'Scala 2 and 3' for=string-inside2 %}
+{% tab 'Scala 2 и 3' for=string-inside2 %}
```scala
println(s"Name: $firstName $mi $lastName") // "Name: John C Doe"
```
@@ -228,7 +228,7 @@ println(s"Name: $firstName $mi $lastName") // "Name: John C Doe"
Чтобы вставить произвольные выражения в строку, они заключаются в фигурные скобки:
{% tabs string-inside3 %}
-{% tab 'Scala 2 and 3' for=string-inside3 %}
+{% tab 'Scala 2 и 3' for=string-inside3 %}
```scala
println(s"2 + 2 = ${2 + 2}") // печатает "2 + 2 = 4"
val x = -1
@@ -249,7 +249,7 @@ println(s"x.abs = ${x.abs}") // печатает "x.abs = 1"
Многострочные строки создаются путем включения строки в три двойные кавычки:
{% tabs string-mlines1 %}
-{% tab 'Scala 2 and 3' for=string-mlines1 %}
+{% tab 'Scala 2 и 3' for=string-mlines1 %}
```scala
val quote = """The essence of Scala:
Fusion of functional and object-oriented
@@ -261,7 +261,7 @@ val quote = """The essence of Scala:
Одним из недостатков базового подхода является то, что строки после первой имеют отступ.
{% tabs string-mlines2 %}
-{% tab 'Scala 2 and 3' for=string-mlines2 %}
+{% tab 'Scala 2 и 3' for=string-mlines2 %}
```scala
"The essence of Scala:
Fusion of functional and object-oriented
@@ -273,7 +273,7 @@ val quote = """The essence of Scala:
Если важно исключить отступ, можно поставить символ `|` перед всеми строками после первой и вызвать метод `stripMargin` после строки:
{% tabs string-mlines3 %}
-{% tab 'Scala 2 and 3' for=string-mlines3 %}
+{% tab 'Scala 2 и 3' for=string-mlines3 %}
```scala
val quote = """The essence of Scala:
|Fusion of functional and object-oriented
@@ -285,7 +285,7 @@ val quote = """The essence of Scala:
Теперь все строки выравниваются по левому краю:
{% tabs string-mlines4 %}
-{% tab 'Scala 2 and 3' for=string-mlines4 %}
+{% tab 'Scala 2 и 3' for=string-mlines4 %}
```scala
"The essence of Scala:
Fusion of functional and object-oriented
@@ -303,7 +303,7 @@ programming in a typed setting."
Например:
{% tabs cast1 %}
-{% tab 'Scala 2 and 3' for=cast1 %}
+{% tab 'Scala 2 и 3' for=cast1 %}
```scala
val b: Byte = 127
val i: Int = b // 127
@@ -318,7 +318,7 @@ val number: Int = face // 9786
В противном случае вам нужно четко указать приведение типов:
{% tabs cast2 %}
-{% tab 'Scala 2 and 3' for=cast2 %}
+{% tab 'Scala 2 и 3' for=cast2 %}
```scala
val x: Long = 987654321
val y: Float = x.toFloat // 9.8765434E8 (обратите внимание, что требуется `.toFloat`, потому что приведение приводит к потере точности)
diff --git a/_ru/scala3/book/fun-anonymous-functions.md b/_ru/scala3/book/fun-anonymous-functions.md
new file mode 100644
index 0000000000..d5d0046917
--- /dev/null
+++ b/_ru/scala3/book/fun-anonymous-functions.md
@@ -0,0 +1,214 @@
+---
+layout: multipage-overview
+title: Анонимные функции
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: section
+description: На этой странице показано, как использовать анонимные функции в Scala, включая примеры с функциями map и filter класса List.
+language: ru
+num: 28
+previous-page: fun-intro
+next-page: fun-function-variables
+---
+
+Анонимная функция, также известная как _лямбда_, представляет собой блок кода,
+который передается в качестве аргумента функции высшего порядка.
+Википедия определяет [анонимную функцию](https://en.wikipedia.org/wiki/Anonymous_function)
+как “определение функции, не привязанное к идентификатору”.
+
+Например, возьмем коллекцию:
+
+{% tabs fun-anonymous-1 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val ints = List(1, 2, 3)
+```
+{% endtab %}
+{% endtabs %}
+
+Можно создать новый список, удвоив каждый элемент в целых числах, используя метод `map` класса `List`
+и свою пользовательскую анонимную функцию:
+
+{% tabs fun-anonymous-2 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val doubledInts = ints.map(_ * 2) // List(2, 4, 6)
+```
+{% endtab %}
+{% endtabs %}
+
+Как видно из комментария, `doubleInts` содержит список `List(2, 4, 6)`.
+В этом примере анонимной функцией является часть кода:
+
+{% tabs fun-anonymous-3 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+_ * 2
+```
+{% endtab %}
+{% endtabs %}
+
+Это сокращенный способ сказать: “Умножить данный элемент на 2”.
+
+## Более длинные формы
+
+Когда вы освоитесь со Scala, то будете постоянно использовать эту форму для написания анонимных функций,
+использующих одну переменную в одном месте функции.
+Но при желании можете также написать их, используя более длинные формы,
+поэтому в дополнение к написанию этого кода:
+
+{% tabs fun-anonymous-4 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val doubledInts = ints.map(_ * 2)
+```
+{% endtab %}
+{% endtabs %}
+
+вы также можете написать его, используя такие формы:
+
+{% tabs fun-anonymous-5 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val doubledInts = ints.map((i: Int) => i * 2)
+val doubledInts = ints.map((i) => i * 2)
+val doubledInts = ints.map(i => i * 2)
+```
+{% endtab %}
+{% endtabs %}
+
+Все эти строки имеют одно и то же значение: удваивайте каждый элемент `ints`, чтобы создать новый список, `doubledInts`
+(синтаксис каждой формы объясняется ниже).
+
+Если вы знакомы с Java, вам будет полезно узнать, что эти примеры `map` эквивалентны следующему Java коду:
+
+{% tabs fun-anonymous-5-b %}
+{% tab 'Java' %}
+```java
+List ints = List.of(1, 2, 3);
+List doubledInts = ints.stream()
+ .map(i -> i * 2)
+ .collect(Collectors.toList());
+```
+{% endtab %}
+{% endtabs %}
+
+## Сокращение анонимных функций
+
+Если необходимо явно указать анонимную функцию, можно использовать следующую длинную форму:
+
+{% tabs fun-anonymous-6 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val doubledInts = ints.map((i: Int) => i * 2)
+```
+{% endtab %}
+{% endtabs %}
+
+Анонимная функция в этом выражении такова:
+
+{% tabs fun-anonymous-7 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+(i: Int) => i * 2
+```
+{% endtab %}
+{% endtabs %}
+
+Если незнаком данный синтаксис, то можно воспринимать символ `=>` как преобразователь,
+потому что выражение _преобразует_ список параметров в левой части символа (переменная `Int` с именем `i`)
+в новый результат, используя алгоритм справа от символа `=>`
+(в данном случае выражение, которое удваивает значение `Int`).
+
+
+### Сокращение выражения
+
+Эту длинную форму можно сократить, как будет показано в следующих шагах.
+Во-первых, вот снова самая длинная и явная форма:
+
+{% tabs fun-anonymous-8 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val doubledInts = ints.map((i: Int) => i * 2)
+```
+{% endtab %}
+{% endtabs %}
+
+Поскольку компилятор Scala может сделать вывод из данных в `ints` о том, что `i` - это `Int`,
+`Int` объявление можно удалить:
+
+{% tabs fun-anonymous-9 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val doubledInts = ints.map((i) => i * 2)
+```
+{% endtab %}
+{% endtabs %}
+
+Поскольку есть только один аргумент, круглые скобки вокруг параметра `i` не нужны:
+
+{% tabs fun-anonymous-10 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val doubledInts = ints.map(i => i * 2)
+```
+{% endtab %}
+{% endtabs %}
+
+Поскольку Scala позволяет использовать символ `_` вместо имени переменной,
+когда параметр появляется в функции только один раз, код можно упростить еще больше:
+
+{% tabs fun-anonymous-11 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val doubledInts = ints.map(_ * 2)
+```
+{% endtab %}
+{% endtabs %}
+
+### Ещё короче
+
+В других примерах можно еще больше упростить анонимные функции.
+Например, начиная с самой явной формы, можно распечатать каждый элемент в `ints`,
+используя эту анонимную функцию с методом `foreach` класса `List`:
+
+{% tabs fun-anonymous-12 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+ints.foreach((i: Int) => println(i))
+```
+{% endtab %}
+{% endtabs %}
+
+Как и раньше, объявление `Int` не требуется, а поскольку аргумент всего один, скобки вокруг `i` не нужны:
+
+{% tabs fun-anonymous-13 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+ints.foreach(i => println(i))
+```
+{% endtab %}
+{% endtabs %}
+
+Поскольку `i` используется в теле функции только один раз, выражение можно еще больше упростить с помощью символа `_`:
+
+{% tabs fun-anonymous-14 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+ints.foreach(println(_))
+```
+{% endtab %}
+{% endtabs %}
+
+Наконец, если анонимная функция состоит из одного вызова метода с одним аргументом,
+нет необходимости явно называть и указывать аргумент,
+можно написать только имя метода (здесь, `println`):
+
+{% tabs fun-anonymous-15 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+ints.foreach(println)
+```
+{% endtab %}
+{% endtabs %}
diff --git a/_ru/scala3/book/fun-eta-expansion.md b/_ru/scala3/book/fun-eta-expansion.md
new file mode 100644
index 0000000000..9e2baf3810
--- /dev/null
+++ b/_ru/scala3/book/fun-eta-expansion.md
@@ -0,0 +1,92 @@
+---
+layout: multipage-overview
+title: Eta расширение
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: section
+description: На этой странице обсуждается Eta Expansion, технология Scala, которая автоматически и прозрачно преобразует методы в функции.
+language: ru
+num: 30
+previous-page: fun-function-variables
+next-page: fun-hofs
+---
+
+
+Если посмотреть на Scaladoc для метода `map` в классах коллекций Scala,
+то можно увидеть, что метод определен для приема _функции_:
+
+```scala
+def map[B](f: (A) => B): List[B]
+ -----------
+```
+
+Действительно, в Scaladoc сказано: “`f` — это _функция_, применяемая к каждому элементу”.
+Но, несмотря на это, каким-то образом в `map` можно передать _метод_, и он все еще работает:
+
+```scala
+def times10(i: Int) = i * 10 // метод
+List(1, 2, 3).map(times10) // List(10,20,30)
+```
+
+Как это работает? Как можно передать _метод_ в `map`, который ожидает _функцию_?
+
+Технология, стоящая за этим, известна как _Eta Expansion_.
+Она преобразует выражение _типа метода_ в эквивалентное выражение _типа функции_, и делает это легко и незаметно.
+
+
+## Различия между методами и функциями
+
+Исторически _методы_ были частью определения класса, хотя в Scala 3 методы могут быть вне классов,
+такие как [определения верхнего уровня][toplevel] и [методы расширения][extension].
+
+В отличие от методов, _функции_ сами по себе являются полноценными объектами, что делает их объектами первого класса.
+
+Их синтаксис также отличается.
+В этом примере показано, как задать метод и функцию, которые выполняют одну и ту же задачу,
+определяя, является ли заданное целое число четным:
+
+```scala
+def isEvenMethod(i: Int) = i % 2 == 0 // метод
+val isEvenFunction = (i: Int) => i % 2 == 0 // функция
+```
+
+Функция действительно является объектом, поэтому ее можно использовать так же,
+как и любую другую переменную, например, помещая в список:
+
+```scala
+val functions = List(isEvenFunction)
+```
+
+И наоборот, технически метод не является объектом, поэтому в Scala 2 метод нельзя было поместить в `List`,
+по крайней мере, напрямую, как показано в этом примере:
+
+```scala
+// В этом примере показано сообщение об ошибке в Scala 2
+val methods = List(isEvenMethod)
+ ^
+error: missing argument list for method isEvenMethod
+Unapplied methods are only converted to functions when a function type is expected.
+You can make this conversion explicit by writing `isEvenMethod _` or `isEvenMethod(_)` instead of `isEvenMethod`.
+```
+
+Как показано в этом сообщении об ошибке, в Scala 2 существует ручной способ преобразования метода в функцию,
+но важной частью для Scala 3 является то, что технология Eta Expansion улучшена,
+поэтому теперь, когда попытаться использовать метод в качестве переменной,
+он просто работает — не нужно самостоятельно выполнять ручное преобразование:
+
+```scala
+val functions = List(isEvenFunction) // работает
+val methods = List(isEvenMethod) // работает
+```
+
+Для целей этой вводной книги важно знать следующее:
+
+- Eta Expansion — технология Scala, позволяющая использовать методы так же, как и функции
+- Технология была улучшена в Scala 3, чтобы быть почти полностью бесшовной
+
+Дополнительные сведения о том, как это работает, см. на [странице Eta Expansion][eta_expansion] в справочной документации.
+
+[eta_expansion]: {{ site.scala3ref }}/changed-features/eta-expansion.html
+[extension]: {% link _overviews/scala3-book/ca-extension-methods.md %}
+[toplevel]: {% link _overviews/scala3-book/taste-toplevel-definitions.md %}
diff --git a/_ru/scala3/book/fun-function-variables.md b/_ru/scala3/book/fun-function-variables.md
new file mode 100644
index 0000000000..bd41113ac1
--- /dev/null
+++ b/_ru/scala3/book/fun-function-variables.md
@@ -0,0 +1,172 @@
+---
+layout: multipage-overview
+title: Параметры функции
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: section
+description: На этой странице показано, как использовать параметры функции в Scala.
+language: ru
+num: 29
+previous-page: fun-anonymous-functions
+next-page: fun-eta-expansion
+---
+
+
+Вернемся к примеру из предыдущего раздела:
+
+{% tabs fun-function-variables-1 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val doubledInts = ints.map((i: Int) => i * 2)
+```
+{% endtab %}
+{% endtabs %}
+
+Анонимной функцией является следующая часть:
+
+{% tabs fun-function-variables-2 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+(i: Int) => i * 2
+```
+{% endtab %}
+{% endtabs %}
+
+Причина, по которой она называется _анонимной_ (_anonymous_), заключается в том,
+что она не присваивается переменной и, следовательно, не имеет имени.
+
+Однако анонимная функция, также известная как _функциональный литерал_ (_function literal_),
+может быть назначена переменной для создания _функциональной переменной_ (_function variable_):
+
+{% tabs fun-function-variables-3 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val double = (i: Int) => i * 2
+```
+{% endtab %}
+{% endtabs %}
+
+Код выше создает функциональную переменную с именем `double`.
+В этом выражении исходный литерал функции находится справа от символа `=`:
+
+{% tabs fun-function-variables-4 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val double = (i: Int) => i * 2
+ -----------------
+```
+{% endtab %}
+{% endtabs %}
+
+, а новое имя переменной - слева:
+
+{% tabs fun-function-variables-5 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val double = (i: Int) => i * 2
+ ------
+```
+{% endtab %}
+{% endtabs %}
+
+список параметров функции подчеркнут:
+
+{% tabs fun-function-variables-6 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val double = (i: Int) => i * 2
+ --------
+```
+{% endtab %}
+{% endtabs %}
+
+Как и список параметров для метода, список параметров функции означает,
+что функция `double` принимает один параметр с типом `Int` и именем `i`.
+Как можно видеть ниже, `double` имеет тип `Int => Int`,
+что означает, что он принимает один параметр `Int` и возвращает `Int`:
+
+{% tabs fun-function-variables-7 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+scala> val double = (i: Int) => i * 2
+val double: Int => Int = ...
+```
+{% endtab %}
+{% endtabs %}
+
+
+### Вызов функции
+
+Функция `double` может быть вызвана так:
+
+{% tabs fun-function-variables-8 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val x = double(2) // 4
+```
+{% endtab %}
+{% endtabs %}
+
+`double` также можно передать в вызов `map`:
+
+{% tabs fun-function-variables-9 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+List(1, 2, 3).map(double) // List(2, 4, 6)
+```
+{% endtab %}
+{% endtabs %}
+
+Кроме того, когда есть другие функции типа `Int => Int`:
+
+{% tabs fun-function-variables-10 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val triple = (i: Int) => i * 3
+```
+{% endtab %}
+{% endtabs %}
+
+можно сохранить их в `List` или `Map`:
+
+{% tabs fun-function-variables-11 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val functionList = List(double, triple)
+
+val functionMap = Map(
+ "2x" -> double,
+ "3x" -> triple
+)
+```
+{% endtab %}
+{% endtabs %}
+
+Если вы вставите эти выражения в REPL, то увидите, что они имеют следующие типы:
+
+{% tabs fun-function-variables-12 %}
+{% tab 'Scala 2 и 3' %}
+````
+// список, содержащий функции типа `Int => Int`
+functionList: List[Int => Int]
+
+// Map, ключи которой имеют тип `String`,
+// а значения имеют тип `Int => Int`
+functionMap: Map[String, Int => Int]
+````
+{% endtab %}
+{% endtabs %}
+
+
+
+## Ключевые моменты
+
+Ключевыми моментами здесь являются:
+
+- чтобы создать функциональную переменную, достаточно присвоить имя переменной функциональному литералу
+- когда есть функция, с ней можно обращаться как с любой другой переменной, то есть как со `String` или `Int` переменной
+
+А благодаря улучшенной функциональности [Eta Expansion][eta_expansion] в Scala 3 с _методами_ можно обращаться точно так же.
+
+[eta_expansion]: {% link _overviews/scala3-book/fun-eta-expansion.md %}
diff --git a/_ru/scala3/book/fun-hofs.md b/_ru/scala3/book/fun-hofs.md
new file mode 100644
index 0000000000..ae8445463c
--- /dev/null
+++ b/_ru/scala3/book/fun-hofs.md
@@ -0,0 +1,389 @@
+---
+layout: multipage-overview
+title: Функции высшего порядка
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: section
+description: На этой странице показано, как создавать и использовать функции высшего порядка в Scala.
+language: ru
+num: 31
+previous-page: fun-eta-expansion
+next-page: fun-write-map-function
+---
+
+
+Функция высшего порядка (HOF - higher-order function) часто определяется как функция, которая
+
+- принимает другие функции в качестве входных параметров или
+- возвращает функцию в качестве результата.
+
+В Scala HOF возможны, потому что функции являются объектами первого класса.
+
+В качестве важного примечания: хотя в этом документе используется общепринятый термин “функция высшего порядка”,
+в Scala эта фраза применима как к методам, так и к функциям.
+Благодаря [технологии Eta Expansion][eta_expansion] их, как правило, можно использовать в одних и тех же местах.
+
+
+## От потребителя к разработчику
+
+В примерах, приведенных ранее в документации, было видно, как _пользоваться_ методами,
+которые принимают другие функции в качестве входных параметров, например, `map` и `filter`.
+
+В следующих разделах будет показано, как _создавать_ HOF, в том числе:
+
+- как писать методы, принимающие функции в качестве входных параметров
+- как возвращать функции из методов
+
+В процессе будет видно:
+
+- синтаксис, который используется для определения входных параметров функции
+- как вызвать функцию, если есть на нее ссылка
+
+В качестве полезного побочного эффекта, как только синтаксис станет привычным,
+его можно начать использовать для определения параметров функций, анонимных функций и функциональных переменных,
+а также станет легче читать Scaladoc для функций высшего порядка.
+
+
+## Понимание Scaladoc метода filter
+
+Чтобы понять, как работают функции высшего порядка, рассмотрим пример:
+определим, какой тип функций принимает `filter`, взглянув на его Scaladoc.
+Вот определение `filter` в классе `List[A]`:
+
+{% tabs filter-definition %}
+{% tab 'Scala 2 и 3' %}
+```scala
+def filter(p: A => Boolean): List[A]
+```
+{% endtab %}
+{% endtabs %}
+
+Это определение указывает на то, что `filter` - метод, который принимает параметр функции с именем `p`.
+По соглашению, `p` обозначает _предикат_, который представляет собой просто функцию, возвращающую `Boolean`.
+Таким образом, `filter` принимает предикат `p` в качестве входного параметра и возвращает `List[A]`,
+где `A` - тип, содержащийся в списке; если `filter` вызывается для `List[Int]`, то `A` - это тип `Int`.
+
+На данный момент, если не учитывать назначение метода `filter`,
+все, что известно, так это то, что алгоритм каким-то образом использует предикат `p` для создания и возврата `List[A]`.
+
+Если посмотреть конкретно на параметр функции `p`:
+
+```scala
+p: A => Boolean
+```
+
+, то эта часть описания `filter` означает, что любая передаваемая функция
+должна принимать тип `A` в качестве входного параметра и возвращать `Boolean`.
+Итак, если список представляет собой список `List[Int]`,
+то можно заменить универсальный тип `A` на `Int` и прочитать эту подпись следующим образом:
+
+```scala
+p: Int => Boolean
+```
+
+Поскольку `isEven` имеет такой же тип — преобразует входное значение `Int` в результирующее `Boolean` —
+его можно использовать с `filter`.
+
+
+## Написание методов, которые принимают параметры функции
+
+Рассмотрим пример написания методов, которые принимают функции в качестве входных параметров.
+
+**Примечание:** для определенности, будем называть код, который пишется, _методом_,
+а код, принимаемый в качестве входного параметра, — _функцией_.
+
+### Пример
+
+Чтобы создать метод, который принимает функцию в качестве параметра, необходимо:
+
+1. в списке параметров метода определить сигнатуру принимаемой функции
+2. использовать эту функцию внутри метода
+
+Чтобы продемонстрировать это, вот метод, который принимает входной параметр с именем `f`, где `f` — функция:
+
+
+{% tabs sayHello-definition %}
+{% tab 'Scala 2 и 3' %}
+```scala
+def sayHello(f: () => Unit): Unit = f()
+```
+{% endtab %}
+{% endtabs %}
+
+Эта часть кода — _сигнатура типа (type signature)_ — утверждает, что `f` является функцией,
+и определяет типы функций, которые будет принимать метод `sayHello`:
+
+```scala
+f: () => Unit
+```
+
+Как это работает:
+
+- `f` — имя входного параметра функции.
+ Аналогично тому, как параметр `String` обычно называется `s` или параметр `Int` - `i`
+- сигнатура типа `f` определяет _тип_ функций, которые будет принимать метод
+- часть `()` подписи `f` (слева от символа `=>`) указывает на то, что `f` не принимает входных параметров
+- часть сигнатуры `Unit` (справа от символа `=>`) указывает на то, что функция `f` не должна возвращать осмысленный результат
+- в теле метода `sayHello` (справа от символа `=`) оператор `f()` вызывает переданную функцию
+
+Теперь, когда `sayHello` определен, создадим функцию, соответствующую сигнатуре `f`, чтобы ее можно было проверить.
+Следующая функция не принимает входных параметров и ничего не возвращает, поэтому она соответствует сигнатуре типа `f`:
+
+{% tabs helloJoe-definition %}
+{% tab 'Scala 2 и 3' %}
+```scala
+def helloJoe(): Unit = println("Hello, Joe")
+```
+{% endtab %}
+{% endtabs %}
+
+
+Поскольку сигнатуры типов совпадают, можно передать `helloJoe` в `sayHello`:
+
+{% tabs sayHello-usage %}
+{% tab 'Scala 2 и 3' %}
+```scala
+sayHello(helloJoe) // печатает "Hello, Joe"
+```
+{% endtab %}
+{% endtabs %}
+
+Если вы никогда этого не делали раньше, поздравляем:
+был определен метод с именем `sayHello`, который принимает функцию в качестве входного параметра,
+а затем вызывает эту функцию в теле своего метода.
+
+
+### sayHello может принимать разные функции
+
+Важно знать, что преимущество этого подхода заключается не в том,
+что `sayHello` может принимать одну функцию в качестве входного параметра;
+преимущество в том, что `sayHello` может принимать любую функцию, соответствующую сигнатуре `f`.
+Например, поскольку следующая функция не принимает входных параметров и ничего не возвращает, она также работает с `sayHello`:
+
+{% tabs bonjourJulien-definition %}
+{% tab 'Scala 2 и 3' %}
+```scala
+def bonjourJulien(): Unit = println("Bonjour, Julien")
+```
+{% endtab %}
+{% endtabs %}
+
+Вот что выводится в REPL:
+
+{% tabs bonjourJulien-usage %}
+{% tab 'Scala 2 и 3' %}
+````
+scala> sayHello(bonjourJulien)
+Bonjour, Julien
+````
+{% endtab %}
+{% endtabs %}
+
+Это отличный старт.
+Рассмотрим ещё несколько примеров того, как определять сигнатуры различных типов для параметров функции.
+
+
+## Общий синтаксис для определения входных параметров функции
+
+В методе:
+
+{% tabs sayHello-definition-2 %}
+{% tab 'Scala 2 и 3' %}
+```scala
+def sayHello(f: () => Unit): Unit
+```
+{% endtab %}
+{% endtabs %}
+
+сигнатурой типа для `f` является:
+
+```scala
+() => Unit
+```
+
+Это сигнатура означает “функцию, которая не принимает входных параметров и не возвращает ничего значимого (`Unit`)”.
+
+Вот сигнатура функции, которая принимает параметр `String` и возвращает `Int`:
+
+```scala
+f: String => Int
+```
+
+Какие функции принимают строку и возвращают целое число?
+Например, такие, как “длина строки” и контрольная сумма.
+
+Эта функция принимает два параметра `Int` и возвращает `Int`:
+
+```scala
+f: (Int, Int) => Int
+```
+
+Какие функции соответствуют данной сигнатуре?
+
+Любая функция, которая принимает два входных параметра `Int` и возвращает `Int`,
+соответствует этой сигнатуре, поэтому все “функции” ниже (точнее, методы) подходят:
+
+{% tabs add-sub-mul-definitions %}
+{% tab 'Scala 2 и 3' %}
+```scala
+def add(a: Int, b: Int): Int = a + b
+def subtract(a: Int, b: Int): Int = a - b
+def multiply(a: Int, b: Int): Int = a * b
+```
+{% endtab %}
+{% endtabs %}
+
+Из примеров выше можно сделать вывод, что общий синтаксис сигнатуры функций такой:
+
+```scala
+variableName: (parameterTypes ...) => returnType
+```
+
+> Поскольку функциональное программирование похоже на создание и объединение ряда алгебраических уравнений,
+> обычно _много думают_ о типах при разработке функций и приложений.
+> Можно сказать, что “думают типами”.
+
+
+## Параметр функции вместе с другими параметрами
+
+Чтобы HOFs стали действительно полезными, им также нужны некоторые данные для работы.
+Для класса, подобного `List`, в его методе `map` уже есть данные для работы: элементы в `List`.
+Но для автономного приложения, у которого нет собственных данных,
+метод также должен принимать в качестве других входных параметров данные.
+
+Рассмотрим пример метода с именем `executeNTimes`, который имеет два входных параметра: функцию и `Int`:
+
+{% tabs executeNTimes-definition class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+```scala
+def executeNTimes(f: () => Unit, n: Int): Unit =
+ for (i <- 1 to n) f()
+```
+{% endtab %}
+{% tab 'Scala 3' %}
+```scala
+def executeNTimes(f: () => Unit, n: Int): Unit =
+ for i <- 1 to n do f()
+```
+{% endtab %}
+{% endtabs %}
+
+Как видно из кода, `executeNTimes` выполняет функцию `f` `n` раз.
+Поскольку простой цикл `for`, подобный этому, не имеет возвращаемого значения, `executeNTimes` возвращает `Unit`.
+
+Чтобы протестировать `executeNTimes`, определим метод, соответствующий сигнатуре `f`:
+
+{% tabs helloWorld-definition %}
+{% tab 'Scala 2 и 3' %}
+```scala
+// тип метода - `() => Unit`
+def helloWorld(): Unit = println("Hello, world")
+```
+{% endtab %}
+{% endtabs %}
+
+Затем передадим этот метод в `executeNTimes` вместе с `Int`:
+
+{% tabs helloWorld-usage %}
+{% tab 'Scala 2 и 3' %}
+```
+scala> executeNTimes(helloWorld, 3)
+Hello, world
+Hello, world
+Hello, world
+```
+{% endtab %}
+{% endtabs %}
+
+
+Великолепно.
+Метод `executeNTimes` трижды выполняет функцию `helloWorld`.
+
+
+### Столько параметров, сколько необходимо
+
+Методы могут усложняться по мере необходимости.
+Например, этот метод принимает функцию типа `(Int, Int) => Int` вместе с двумя входными параметрами:
+
+{% tabs executeAndPrint-definition %}
+{% tab 'Scala 2 и 3' %}
+```scala
+def executeAndPrint(f: (Int, Int) => Int, i: Int, j: Int): Unit =
+ println(f(i, j))
+```
+{% endtab %}
+{% endtabs %}
+
+
+Поскольку методы `sum` и `multiply` соответствуют сигнатуре `f`,
+их можно передать в `executeAndPrint` вместе с двумя значениями `Int`:
+
+{% tabs executeAndPrint-usage %}
+{% tab 'Scala 2 и 3' %}
+```scala
+def sum(x: Int, y: Int) = x + y
+def multiply(x: Int, y: Int) = x * y
+
+executeAndPrint(sum, 3, 11) // печатает 14
+executeAndPrint(multiply, 3, 9) // печатает 27
+```
+{% endtab %}
+{% endtabs %}
+
+
+## Согласованность подписи типа функции
+
+Самое замечательное в изучении сигнатур типов функций Scala заключается в том,
+что синтаксис, используемый для определения входных параметров функции, —
+это тот же синтаксис, что используется для написания литералов функций.
+
+Например, если необходимо написать функцию, вычисляющую сумму двух целых чисел, её можно было бы написать так:
+
+{% tabs f-val-definition %}
+{% tab 'Scala 2 и 3' %}
+```scala
+val f: (Int, Int) => Int = (a, b) => a + b
+```
+{% endtab %}
+{% endtabs %}
+
+Этот код состоит из сигнатуры типа:
+
+````
+val f: (Int, Int) => Int = (a, b) => a + b
+ -----------------
+````
+
+входных параметров:
+
+````
+val f: (Int, Int) => Int = (a, b) => a + b
+ ------
+````
+
+и тела функции:
+
+````
+val f: (Int, Int) => Int = (a, b) => a + b
+ -----
+````
+
+Согласованность Scala состоит в том, что тип функции:
+
+````
+val f: (Int, Int) => Int = (a, b) => a + b
+ -----------------
+````
+
+совпадает с сигнатурой типа, используемого для определения входного параметра функции:
+
+````
+def executeAndPrint(f: (Int, Int) => Int, ...
+ -----------------
+````
+
+По мере освоения этого синтаксиса, становится привычным его использование для определения параметров функций,
+анонимных функций и функциональных переменных, а также становится легче читать Scaladoc для функций высшего порядка.
+
+[eta_expansion]: {% link _overviews/scala3-book/fun-eta-expansion.md %}
diff --git a/_ru/scala3/book/fun-intro.md b/_ru/scala3/book/fun-intro.md
new file mode 100644
index 0000000000..e28840f949
--- /dev/null
+++ b/_ru/scala3/book/fun-intro.md
@@ -0,0 +1,17 @@
+---
+layout: multipage-overview
+title: Функции
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: chapter
+description: В этой главе рассматриваются темы, связанные с функциями в Scala 3.
+language: ru
+num: 27
+previous-page: methods-summary
+next-page: fun-anonymous-functions
+---
+
+Если в предыдущей главе были представлены Scala _методы_, то в этой главе мы углубимся в _функции_.
+Рассматриваемые темы включают анонимные функции, функциональные переменные и функции высшего порядка (HOF),
+в том числе способы создания собственных HOF.
diff --git a/_ru/scala3/book/fun-summary.md b/_ru/scala3/book/fun-summary.md
new file mode 100644
index 0000000000..497e4d4c5e
--- /dev/null
+++ b/_ru/scala3/book/fun-summary.md
@@ -0,0 +1,41 @@
+---
+layout: multipage-overview
+title: Обзор
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: section
+description: На этой странице представлен обзор предыдущего раздела 'Функции'.
+language: ru
+num: 34
+previous-page: fun-write-method-returns-function
+next-page:
+---
+
+Это была длинная глава, поэтому давайте рассмотрим ключевые моменты, которые мы прошли.
+
+Функция высшего порядка (HOF) часто определяется как функция,
+которая принимает другие функции в качестве входных параметров или возвращает функцию в качестве своего значения.
+В Scala это возможно, потому что функции являются объектами первого класса.
+
+Двигаясь по разделам, сначала вы узнали:
+
+- Как писать анонимные функции в виде небольших фрагментов кода.
+- Как передать их десяткам HOF (методов) в классах коллекций, т.е. таким методам, как `filter`, `map` и т.д.
+- Как с помощью этих небольших фрагментов кода и мощных HOF создавать множество функций с помощью небольшого кода.
+
+Изучив анонимные функции и HOF, вы узнали:
+
+- Функциональные переменные — это просто анонимные функции, привязанные к переменной.
+
+Увидев, как быть потребителем HOF, вы увидели, как стать создателем HOF.
+В частности, вы узнали:
+
+- Как писать методы, принимающие функции в качестве входных параметров
+- Как вернуть функцию из метода
+
+Полезным побочным эффектом этой главы является то,
+что вы увидели много примеров того, как объявлять сигнатуры типов для функций.
+Преимущество этого заключается в том, что вы используете один и тот же синтаксис
+для определения параметров функций, анонимных функций и функциональных переменных,
+а также становится легче читать Scaladoc для функций высшего порядка, таких как `map`, `filter` и другие.
diff --git a/_ru/scala3/book/fun-write-map-function.md b/_ru/scala3/book/fun-write-map-function.md
new file mode 100644
index 0000000000..020a2e61a8
--- /dev/null
+++ b/_ru/scala3/book/fun-write-map-function.md
@@ -0,0 +1,141 @@
+---
+layout: multipage-overview
+title: Собственный map
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: section
+description: На этой странице описано, как создать свой собственный метод map
+language: ru
+num: 32
+previous-page: fun-hofs
+next-page: fun-write-method-returns-function
+---
+
+
+Теперь, когда известно, как писать собственные функции высшего порядка, рассмотрим более реальный пример.
+
+Представим, что у класса `List` нет метода `map`, и есть необходимость его написать.
+Первым шагом при создании функций является точное определение проблемы.
+Сосредоточившись только на `List[Int]`, получаем:
+
+> Необходимо написать метод `map`, который можно использовать для применения функции к каждому элементу в `List[Int]`,
+> возвращая преобразованные элементы в виде нового списка.
+
+Учитывая это утверждение, начнем писать сигнатуру метода.
+Во-первых, известно, что функция должна приниматься в качестве параметра,
+и эта функция должна преобразовать `Int` в какой-то общий тип `A`, поэтому получаем:
+
+{% tabs map-accept-func-definition %}
+{% tab 'Scala 2 и 3' %}
+```scala
+def map(f: (Int) => A)
+```
+{% endtab %}
+{% endtabs %}
+
+Синтаксис использования универсального типа требует объявления этого символа типа перед списком параметров,
+поэтому добавляем объявление типа:
+
+{% tabs map-type-symbol-definition %}
+{% tab 'Scala 2 и 3' %}
+```scala
+def map[A](f: (Int) => A)
+```
+{% endtab %}
+{% endtabs %}
+
+Далее известно, что `map` также должен принимать `List[Int]`:
+
+{% tabs map-list-int-param-definition %}
+{% tab 'Scala 2 и 3' %}
+```scala
+def map[A](f: (Int) => A, xs: List[Int])
+```
+{% endtab %}
+{% endtabs %}
+
+Наконец, также известно, что `map` возвращает преобразованный список, содержащий элементы универсального типа `A`:
+
+{% tabs map-with-return-type-definition %}
+{% tab 'Scala 2 и 3' %}
+```scala
+def map[A](f: (Int) => A, xs: List[Int]): List[A] = ???
+```
+{% endtab %}
+{% endtabs %}
+
+Теперь все, что нужно сделать, это написать тело метода.
+Метод `map` применяет заданную им функцию к каждому элементу в заданном списке для создания нового преобразованного списка.
+Один из способов сделать это - использовать выражение `for`:
+
+{% tabs for-definition class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+```scala
+for (x <- xs) yield f(x)
+```
+{% endtab %}
+{% tab 'Scala 3' %}
+```scala
+for x <- xs yield f(x)
+```
+{% endtab %}
+{% endtabs %}
+
+`for` выражения зачастую делают код удивительно простым, и в данном случае - это все тело метода.
+
+Объединив `for` с сигнатурой метода, получим автономный метод `map`, который работает с `List[Int]`:
+
+{% tabs map-function class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+```scala
+def map[A](f: (Int) => A, xs: List[Int]): List[A] =
+ for (x <- xs) yield f(x)
+```
+{% endtab %}
+{% tab 'Scala 3' %}
+```scala
+def map[A](f: (Int) => A, xs: List[Int]): List[A] =
+ for x <- xs yield f(x)
+```
+{% endtab %}
+{% endtabs %}
+
+
+### Обобщим метод map
+
+Обратим внимание, что выражение `for` не делает ничего, что зависит от типа `Int` внутри списка.
+Следовательно, можно заменить `Int` в сигнатуре типа параметром универсального типа `B`:
+
+{% tabs map-function-full-generic class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+```scala
+def map[A, B](f: (B) => A, xs: List[B]): List[A] =
+ for (x <- xs) yield f(x)
+```
+{% endtab %}
+{% tab 'Scala 3' %}
+```scala
+def map[A, B](f: (B) => A, xs: List[B]): List[A] =
+ for x <- xs yield f(x)
+```
+{% endtab %}
+{% endtabs %}
+
+Получился метод `map`, который работает с любым списком.
+
+Демонстрация работы получившегося `map`:
+
+{% tabs map-use-example %}
+{% tab 'Scala 2 и 3' %}
+```scala
+def double(i : Int): Int = i * 2
+map(double, List(1, 2, 3)) // List(2, 4, 6)
+
+def strlen(s: String): Int = s.length
+map(strlen, List("a", "bb", "ccc")) // List(1, 2, 3)
+```
+{% endtab %}
+{% endtabs %}
+
+Теперь, когда рассмотрены методы, принимающие функции в качестве входных параметров, перейдем к методам, возвращающим функции.
diff --git a/_ru/scala3/book/fun-write-method-returns-function.md b/_ru/scala3/book/fun-write-method-returns-function.md
new file mode 100644
index 0000000000..a322162e58
--- /dev/null
+++ b/_ru/scala3/book/fun-write-method-returns-function.md
@@ -0,0 +1,176 @@
+---
+layout: multipage-overview
+title: Создание метода, возвращающего функцию
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: section
+description: На этой странице показано, как создавать методы, возвращающие функции, в Scala.
+language: ru
+num: 33
+previous-page: fun-write-map-function
+next-page: fun-summary
+---
+
+
+Благодаря согласованности Scala написание метода, возвращающего функцию, похоже на то, что было описано в предыдущих разделах.
+Например, представьте, что вы хотите написать метод `greet`, возвращающий функцию.
+Еще раз начнем с постановки проблемы:
+
+> Необходимо создать метод greet, возвращающий функцию.
+> Эта функция должна принимать строковый параметр и печатать его с помощью `println`.
+> Начнем с простого шага: `greet` не принимает никаких входных параметров, а просто создает функцию и возвращает её.
+
+Учитывая это утверждение, можно начать создавать `greet`.
+Известно, что это будет метод:
+
+```scala
+def greet()
+```
+
+Также известно, что этот метод должен возвращать функцию, которая (a) принимает параметр `String` и
+(b) печатает эту строку с помощью `println`.
+
+Следовательно, эта функция имеет тип `String => Unit`:
+
+```scala
+def greet(): String => Unit = ???
+ ----------------
+```
+
+Теперь нужно просто создать тело метода.
+Известно, что метод должен возвращать функцию, и эта функция принимает `String` и печатает ее.
+Эта анонимная функция соответствует следующему описанию:
+
+```scala
+(name: String) => println(s"Hello, $name")
+```
+
+Теперь вы просто возвращаете эту функцию из метода:
+
+```scala
+// метод, который возвращает функцию
+def greet(): String => Unit =
+ (name: String) => println(s"Hello, $name")
+```
+
+Поскольку этот метод возвращает функцию, вы получаете функцию, вызывая `greet()`.
+Это хороший шаг для проверки в REPL, потому что он проверяет тип новой функции:
+
+````
+scala> val greetFunction = greet()
+val greetFunction: String => Unit = Lambda....
+ -----------------------------
+````
+
+Теперь можно вызвать `greetFunction`:
+
+```scala
+greetFunction("Joe") // печатает "Hello, Joe"
+```
+
+Поздравляем, вы только что создали метод, возвращающий функцию, а затем запустили её.
+
+
+## Доработка метода
+
+Метод `greet` был бы более полезным, если бы была возможность задавать приветствие.
+Например, передать его в качестве параметра методу `greet` и использовать внутри `println`:
+
+```scala
+def greet(theGreeting: String): String => Unit =
+ (name: String) => println(s"$theGreeting, $name")
+```
+
+Теперь, при вызове этого метода, процесс становится более гибким, потому что приветствие можно изменить.
+Вот как это выглядит, когда создается функция из этого метода:
+
+````
+scala> val sayHello = greet("Hello")
+val sayHello: String => Unit = Lambda.....
+ ------------------------
+````
+
+Выходные данные подписи типа показывают, что `sayHello` — это функция,
+которая принимает входной параметр `String` и возвращает `Unit` (ничего).
+Так что теперь, при передаче `sayHello` строки, печатается приветствие:
+
+```scala
+sayHello("Joe") // печатает "Hello, Joe"
+```
+
+Приветствие можно менять для создания новых функций:
+
+```scala
+val sayCiao = greet("Ciao")
+val sayHola = greet("Hola")
+
+sayCiao("Isabella") // печатает "Ciao, Isabella"
+sayHola("Carlos") // печатает "Hola, Carlos"
+```
+
+
+## Более реалистичный пример
+
+Этот метод может быть еще более полезным, когда возвращает одну из многих возможных функций,
+например, фабрику пользовательских функций.
+
+Например, представим, что необходимо написать метод, который возвращает функции, приветствующие людей на разных языках.
+Ограничим это функциями, которые приветствуют на английском или французском языках,
+в зависимости от параметра, переданного в метод.
+
+Созданный метод должен: (a) принимать “желаемый язык” в качестве входных данных
+и (b) возвращать функцию в качестве результата.
+Кроме того, поскольку эта функция печатает заданную строку, известно, что она имеет тип `String => Unit`.
+С помощью этой информации сигнатура метода должна выглядеть так:
+
+```scala
+def createGreetingFunction(desiredLanguage: String): String => Unit = ???
+```
+
+Далее, поскольку возвращаемые функции, берут строку и печатают ее,
+можно прикинуть две анонимные функции для английского и французского языков:
+
+```scala
+(name: String) => println(s"Hello, $name")
+(name: String) => println(s"Bonjour, $name")
+```
+
+Для большей читабельности дадим этим анонимным функциям имена и назначим двум переменным:
+
+```scala
+val englishGreeting = (name: String) => println(s"Hello, $name")
+val frenchGreeting = (name: String) => println(s"Bonjour, $name")
+```
+
+Теперь все, что осталось, это (a) вернуть `englishGreeting`, если `desiredLanguage` — английский,
+и (b) вернуть `frenchGreeting`, если `desiredLanguage` — французский.
+Один из способов сделать это - выражение `match`:
+
+```scala
+def createGreetingFunction(desiredLanguage: String): String => Unit =
+ val englishGreeting = (name: String) => println(s"Hello, $name")
+ val frenchGreeting = (name: String) => println(s"Bonjour, $name")
+ desiredLanguage match
+ case "english" => englishGreeting
+ case "french" => frenchGreeting
+```
+
+И это последний метод.
+Обратите внимание, что возврат значения функции из метода ничем не отличается от возврата строкового или целочисленного значения.
+
+Вот как `createGreetingFunction` создает функцию приветствия на французском языке:
+
+```scala
+val greetInFrench = createGreetingFunction("french")
+greetInFrench("Jonathan") // печатает "Bonjour, Jonathan"
+```
+
+И вот как - на английском:
+
+```scala
+val greetInEnglish = createGreetingFunction("english")
+greetInEnglish("Joe") // печатает "Hello, Joe"
+```
+
+Если вам понятен этот код — поздравляю — теперь вы знаете, как писать методы, возвращающие функции.
diff --git a/_ru/scala3/book/methods-intro.md b/_ru/scala3/book/methods-intro.md
new file mode 100644
index 0000000000..b5192db1cb
--- /dev/null
+++ b/_ru/scala3/book/methods-intro.md
@@ -0,0 +1,22 @@
+---
+layout: multipage-overview
+title: Методы
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: chapter
+description: В этой главе представлены методы в Scala 3.
+language: ru
+num: 23
+previous-page: domain-modeling-fp
+next-page: methods-most
+---
+
+
+В Scala 2 _методы_ могут быть определены внутри классов, трейтов, объектов, `case` классов и `case` объектов.
+Но стало еще лучше: в Scala 3 они также могут быть определены вне любой из этих конструкций;
+мы говорим, что это определения "верхнего уровня", поскольку они не вложены в другое определение.
+Короче говоря, теперь методы можно определять где угодно.
+
+Многие особенности методов демонстрируются в следующем разделе.
+Поскольку `main` методы требуют немного больше пояснений, они описаны в одном из следующих разделов отдельно.
diff --git a/_ru/scala3/book/methods-main-methods.md b/_ru/scala3/book/methods-main-methods.md
new file mode 100644
index 0000000000..3faca33023
--- /dev/null
+++ b/_ru/scala3/book/methods-main-methods.md
@@ -0,0 +1,178 @@
+---
+layout: multipage-overview
+title: Main методы в Scala 3
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: section
+description: На этой странице описывается, как основные методы и аннотация @main работают в Scala 3.
+language: ru
+num: 25
+previous-page: methods-most
+next-page: methods-summary
+---
+
+
Написание однострочных программ только в Scala 3
+
+Scala 3 предлагает следующий способ определения программ, которые можно вызывать из командной строки:
+добавление аннотации `@main` к методу превращает его в точку входа исполняемой программы:
+
+{% tabs method_1 %}
+{% tab 'Только в Scala 3' for=method_1 %}
+
+```scala
+@main def hello() = println("Hello, World")
+```
+
+{% endtab %}
+{% endtabs %}
+
+Для запуска программы достаточно сохранить эту строку кода в файле с именем, например, _Hello.scala_
+(имя файла необязательно должно совпадать с именем метода) и запустить с помощью `scala`:
+
+```bash
+$ scala Hello.scala
+Hello, World
+```
+
+Аннотированный метод `@main` может быть написан либо на верхнем уровне (как показано),
+либо внутри статически доступного объекта.
+В любом случае имя программы - это имя метода без каких-либо префиксов объектов.
+
+Узнайте больше об аннотации `@main`, прочитав следующие разделы или посмотрев это видео:
+
+
+
+
+
+### Аргументы командной строки
+
+Метод `@main` может обрабатывать аргументы командной строки с различными типами.
+Например, данный метод `@main`, который принимает параметры `Int`, `String` и дополнительные строковые параметры:
+
+{% tabs method_2 %}
+{% tab 'Только в Scala 3' for=method_2 %}
+
+```scala
+@main def happyBirthday(age: Int, name: String, others: String*) =
+ val suffix = (age % 100) match
+ case 11 | 12 | 13 => "th"
+ case _ => (age % 10) match
+ case 1 => "st"
+ case 2 => "nd"
+ case 3 => "rd"
+ case _ => "th"
+
+ val sb = StringBuilder(s"Happy $age$suffix birthday, $name")
+ for other <- others do sb.append(" and ").append(other)
+ println(sb.toString)
+```
+
+{% endtab %}
+{% endtabs %}
+
+После компиляции кода создается основная программа с именем `happyBirthday`, которая вызывается следующим образом:
+
+```
+$ scala happyBirthday 23 Lisa Peter
+Happy 23rd Birthday, Lisa and Peter!
+```
+
+Как показано, метод `@main` может иметь произвольное количество параметров.
+Для каждого типа параметра должен существовать [given экземпляр][given]
+класса типа `scala.util.CommandLineParser.FromString`, который преобразует аргумент из `String` в требуемый тип параметра.
+Также, как показано, список параметров основного метода может заканчиваться повторяющимся параметром типа `String*`,
+который принимает все оставшиеся аргументы, указанные в командной строке.
+
+Программа, реализованная с помощью метода `@main`, проверяет,
+что в командной строке достаточно аргументов для заполнения всех параметров,
+и что строки аргументов могут быть преобразованы в требуемые типы.
+Если проверка завершается неудачей, программа завершается с сообщением об ошибке:
+
+```
+$ scala happyBirthday 22
+Illegal command line after first argument: more arguments expected
+
+$ scala happyBirthday sixty Fred
+Illegal command line: java.lang.NumberFormatException: For input string: "sixty"
+```
+
+## Детали
+
+Компилятор Scala генерирует программу из `@main` метода `f` следующим образом:
+
+- он создает класс с именем `f` в пакете, где был найден метод `@main`.
+- класс имеет статический метод `main` с обычной сигнатурой Java `main` метода:
+ принимает `Array[String]` в качестве аргумента и возвращает `Unit`.
+- сгенерированный `main` метод вызывает метод `f` с аргументами,
+ преобразованными с помощью методов в объекте `scala.util.CommandLineParser.FromString`.
+
+Например, приведенный выше метод `happyBirthday` генерирует дополнительный код, эквивалентный следующему классу:
+
+{% tabs method_3 %}
+{% tab 'Только в Scala 3' for=method_3 %}
+
+```scala
+final class happyBirthday {
+ import scala.util.{CommandLineParser as CLP}
+ def main(args: Array[String]): Unit =
+ try
+ happyBirthday(
+ CLP.parseArgument[Int](args, 0),
+ CLP.parseArgument[String](args, 1),
+ CLP.parseRemainingArguments[String](args, 2)*)
+ catch {
+ case error: CLP.ParseError => CLP.showError(error)
+ }
+}
+```
+
+> Примечание: В этом сгенерированном коде модификатор `` выражает,
+> что `main` метод генерируется как статический метод класса `happyBirthday`.
+> Эта функция недоступна для пользовательских программ в Scala.
+> Вместо неё обычные “статические” члены генерируются в Scala с использованием `object`.
+
+{% endtab %}
+{% endtabs %}
+
+## Обратная совместимость со Scala 2
+
+`@main` методы — это рекомендуемый способ создания программ, вызываемых из командной строки в Scala 3.
+Они заменяют предыдущий подход, который заключался в создании `object`, расширяющего класс `App`:
+
+Прежняя функциональность `App`, основанная на "волшебном" `DelayedInit trait`, больше недоступна.
+`App` все еще существует в ограниченной форме, но не поддерживает аргументы командной строки и будет объявлен устаревшим в будущем.
+
+Если программам необходимо выполнять перекрестную сборку между Scala 2 и Scala 3,
+вместо этого рекомендуется использовать `object` с явным методом `main` и одним аргументом `Array[String]`:
+
+{% tabs method_4 %}
+{% tab 'Scala 2 и 3' %}
+
+```scala
+object happyBirthday {
+ private def happyBirthday(age: Int, name: String, others: String*) = {
+ ... // тоже, что и раньше
+ }
+ def main(args: Array[String]): Unit =
+ happyBirthday(args(0).toInt, args(1), args.drop(2).toIndexedSeq:_*)
+}
+```
+
+> обратите внимание, что здесь мы используем `:_*` для передачи переменного числа аргументов,
+> который остается в Scala 3 для обратной совместимости.
+
+{% endtab %}
+{% endtabs %}
+
+Если вы поместите этот код в файл с именем _happyBirthday.scala_, то сможете скомпилировать его с `scalac`
+и запустить с помощью `scala`, как показывалось ранее:
+
+```bash
+$ scalac happyBirthday.scala
+
+$ scala happyBirthday 23 Lisa Peter
+Happy 23rd Birthday, Lisa and Peter!
+```
+
+[given]: {% link _overviews/scala3-book/ca-given-using-clauses.md %}
diff --git a/_ru/scala3/book/methods-most.md b/_ru/scala3/book/methods-most.md
new file mode 100644
index 0000000000..0554fa9464
--- /dev/null
+++ b/_ru/scala3/book/methods-most.md
@@ -0,0 +1,712 @@
+---
+layout: multipage-overview
+title: Особенности методов
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: section
+description: В этом разделе представлены методы Scala 3, включая main методы, методы расширения и многое другое.
+language: ru
+num: 24
+previous-page: methods-intro
+next-page: methods-main-methods
+---
+
+В этом разделе представлены различные аспекты определения и вызова методов в Scala 3.
+
+## Определение методов
+
+В Scala методы обладают множеством особенностей, в том числе:
+
+- Generic (типовые) параметры
+- Значения параметров по умолчанию
+- Несколько групп параметров
+- Контекстные параметры
+- Параметры по имени
+- и другие…
+
+Некоторые из этих функций демонстрируются в этом разделе, но когда вы определяете “простой” метод,
+который не использует эти функции, синтаксис выглядит следующим образом:
+
+{% tabs method_1 class=tabs-scala-version %}
+{% tab 'Scala 2' for=method_1 %}
+
+```scala
+def methodName(param1: Type1, param2: Type2): ReturnType = {
+ // тело метода
+ // находится здесь
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=method_1 %}
+
+```scala
+def methodName(param1: Type1, param2: Type2): ReturnType =
+ // тело метода
+ // находится здесь
+end methodName // опционально
+```
+
+{% endtab %}
+{% endtabs %}
+
+В этом синтаксисе:
+
+- ключевое слово `def` используется для определения метода
+- для наименования методов согласно стандартам Scala используется camel case convention
+- у параметров метода необходимо всегда указывать тип
+- возвращаемый тип метода указывать необязательно
+- методы могут состоять как только из одной строки, так и из нескольких строк
+- метку окончания метода `end methodName` указывать необязательно, её рекомендуется указывать только для длинных методов
+
+Вот два примера однострочного метода с именем `add`, который принимает два входных параметра `Int`.
+Первая версия явно показывает возвращаемый тип метода - `Int`, а вторая - нет:
+
+{% tabs method_2 %}
+{% tab 'Scala 2 и 3' for=method_2 %}
+
+```scala
+def add(a: Int, b: Int): Int = a + b
+def add(a: Int, b: Int) = a + b
+```
+
+{% endtab %}
+{% endtabs %}
+
+У публичных методов рекомендуется всегда указывать тип возвращаемого значения.
+Объявление возвращаемого типа может упростить его понимание при просмотре кода другого человека
+или своего кода спустя некоторое время.
+
+## Вызов методов
+
+Вызов методов прост:
+
+{% tabs method_3 %}
+{% tab 'Scala 2 и 3' for=method_3 %}
+
+```scala
+val x = add(1, 2) // 3
+```
+
+{% endtab %}
+{% endtabs %}
+
+Коллекции Scala имеют десятки встроенных методов.
+Эти примеры показывают, как их вызывать:
+
+{% tabs method_4 %}
+{% tab 'Scala 2 и 3' for=method_4 %}
+
+```scala
+val x = List(1, 2, 3)
+
+x.size // 3
+x.contains(1) // true
+x.map(_ * 10) // List(10, 20, 30)
+```
+
+{% endtab %}
+{% endtabs %}
+
+Внимание:
+
+- `size` не принимает аргументов и возвращает количество элементов в списке
+- метод `contains` принимает один аргумент — значение для поиска
+- `map` принимает один аргумент - функцию; в данном случае в него передается анонимная функция
+
+## Многострочные методы
+
+Если метод длиннее одной строки, начинайте тело метода со второй строки с отступом вправо:
+
+{% tabs method_5 class=tabs-scala-version %}
+{% tab 'Scala 2' for=method_5 %}
+
+```scala
+def addThenDouble(a: Int, b: Int): Int = {
+ // представим, что это тело метода требует несколько строк
+ val sum = a + b
+ sum * 2
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=method_5 %}
+
+```scala
+def addThenDouble(a: Int, b: Int): Int =
+ // представим, что это тело метода требует несколько строк
+ val sum = a + b
+ sum * 2
+```
+
+{% endtab %}
+{% endtabs %}
+
+В этом методе:
+
+- `sum` — неизменяемая локальная переменная; к ней нельзя получить доступ вне метода
+- последняя строка удваивает значение `sum` - именно это значение возвращается из метода
+
+Когда вы вставите этот код в REPL, то увидите, что он работает как требовалось:
+
+{% tabs method_6 %}
+{% tab 'Scala 2 и 3' for=method_6 %}
+
+```scala
+scala> addThenDouble(1, 1)
+res0: Int = 4
+```
+
+{% endtab %}
+{% endtabs %}
+
+Обратите внимание, что нет необходимости в операторе `return` в конце метода.
+Поскольку почти все в Scala является _выражением_ — то это означает,
+что каждая строка кода возвращает (или _вычисляет_) значение — нет необходимости использовать `return`.
+
+Это видно на примере того же метода, но в более сжатой форме:
+
+{% tabs method_7 %}
+{% tab 'Scala 2 и 3' for=method_7 %}
+
+```scala
+def addThenDouble(a: Int, b: Int): Int = (a + b) * 2
+```
+
+{% endtab %}
+{% endtabs %}
+
+В теле метода можно использовать все возможности Scala:
+
+- `if`/`else` выражения
+- `match` выражения
+- циклы `while`
+- циклы `for` и `for` выражения
+- присвоение переменных
+- вызовы других методов
+- определения других методов
+
+В качестве ещё одного примера многострочного метода,
+`getStackTraceAsString` преобразует свой входной параметр `Throwable` в правильно отформатированную строку:
+
+{% tabs method_8 class=tabs-scala-version %}
+{% tab 'Scala 2' for=method_8 %}
+
+```scala
+def getStackTraceAsString(t: Throwable): String = {
+ val sw = new StringWriter()
+ t.printStackTrace(new PrintWriter(sw))
+ sw.toString
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=method_8 %}
+
+```scala
+def getStackTraceAsString(t: Throwable): String =
+ val sw = StringWriter()
+ t.printStackTrace(PrintWriter(sw))
+ sw.toString
+```
+
+{% endtab %}
+{% endtabs %}
+
+В этом методе:
+
+- в первой строке переменная `sw` принимает значение нового экземпляра `StringWriter`
+- вторая строка сохраняет содержимое трассировки стека в `StringWriter`
+- третья строка возвращает строковое представление трассировки стека
+
+## Значения параметров по умолчанию
+
+Параметры метода могут иметь значения по умолчанию.
+В этом примере для параметров `timeout` и `protocol` заданы значения по умолчанию:
+
+{% tabs method_9 class=tabs-scala-version %}
+{% tab 'Scala 2' for=method_9 %}
+
+```scala
+def makeConnection(timeout: Int = 5_000, protocol: String = "http") = {
+ println(f"timeout = ${timeout}%d, protocol = ${protocol}%s")
+ // здесь ещё какой-то код ...
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=method_9 %}
+
+```scala
+def makeConnection(timeout: Int = 5_000, protocol: String = "http") =
+ println(f"timeout = ${timeout}%d, protocol = ${protocol}%s")
+ // здесь ещё какой-то код ...
+```
+
+{% endtab %}
+{% endtabs %}
+
+Поскольку параметры имеют значения по умолчанию, метод можно вызвать следующими способами:
+
+{% tabs method_10 %}
+{% tab 'Scala 2 и 3' for=method_10 %}
+
+```scala
+makeConnection() // timeout = 5000, protocol = http
+makeConnection(2_000) // timeout = 2000, protocol = http
+makeConnection(3_000, "https") // timeout = 3000, protocol = https
+```
+
+{% endtab %}
+{% endtabs %}
+
+Вот несколько ключевых моментов об этих примерах:
+
+- В первом примере аргументы не предоставляются, поэтому метод использует значения параметров по умолчанию: `5_000` и `http`
+- Во втором примере для параметра `timeout` указывается значение `2_000`,
+ поэтому оно используется вместе со значением по умолчанию для `protocol`
+- В третьем примере значения указаны для обоих параметров, поэтому используются они.
+
+Обратите внимание, что при использовании значений параметров по умолчанию потребителю кажется,
+что он может работать с тремя разными переопределенными методами.
+
+## Именованные параметры
+
+При желании вы также можете использовать имена параметров метода при его вызове.
+Например, `makeConnection` может также вызываться следующими способами:
+
+{% tabs method_11 %}
+{% tab 'Scala 2 и 3' for=method_11 %}
+
+```scala
+makeConnection(timeout=10_000)
+makeConnection(protocol="https")
+makeConnection(timeout=10_000, protocol="https")
+makeConnection(protocol="https", timeout=10_000)
+```
+
+{% endtab %}
+{% endtabs %}
+
+В некоторых фреймворках именованные параметры используются постоянно.
+Они также очень полезны, когда несколько параметров метода имеют один и тот же тип:
+
+{% tabs method_12 %}
+{% tab 'Scala 2 и 3' for=method_12 %}
+
+```scala
+engage(true, true, true, false)
+```
+
+{% endtab %}
+{% endtabs %}
+
+Без помощи IDE этот код может быть трудночитаемым, но так он становится намного понятнее и очевиднее:
+
+{% tabs method_13 %}
+{% tab 'Scala 2 и 3' for=method_13 %}
+
+```scala
+engage(
+ speedIsSet = true,
+ directionIsSet = true,
+ picardSaidMakeItSo = true,
+ turnedOffParkingBrake = false
+)
+```
+
+{% endtab %}
+{% endtabs %}
+
+## Рекомендации о методах, которые не принимают параметров
+
+Когда метод не принимает параметров, говорят, что он имеет _arity_ уровень 0 (_arity-0_).
+Аналогично, если метод принимает один параметр - это метод с _arity-1_.
+
+Когда создаются методы _arity-0_:
+
+- если метод выполняет побочные эффекты, такие как вызов `println`, метод объявляется с пустыми скобками.
+- если метод не выполняет побочных эффектов, например, получение размера коллекции,
+ что аналогично доступу к полю в коллекции, круглые скобки опускаются.
+
+Например, этот метод выполняет побочный эффект, поэтому он объявлен с пустыми скобками:
+
+{% tabs method_14 %}
+{% tab 'Scala 2 и 3' for=method_14 %}
+
+```scala
+def speak() = println("hi")
+```
+
+{% endtab %}
+{% endtabs %}
+
+При вызове метода нужно обязательно указывать круглые скобки, если он был объявлен с ними:
+
+{% tabs method_15 %}
+{% tab 'Scala 2 и 3' for=method_15 %}
+
+```scala
+speak // ошибка: "method speak must be called with () argument"
+speak() // печатает "hi"
+```
+
+{% endtab %}
+{% endtabs %}
+
+Хотя это всего лишь соглашение, его соблюдение значительно улучшает читаемость кода:
+с первого взгляда становится понятно, что метод с arity-0 имеет побочные эффекты.
+
+## Использование `if` в качестве тела метода
+
+Поскольку выражения `if`/`else` возвращают значение, их можно использовать в качестве тела метода.
+Вот метод с именем `isTruthy`, реализующий Perl-определения `true` и `false`:
+
+{% tabs method_16 class=tabs-scala-version %}
+{% tab 'Scala 2' for=method_16 %}
+
+```scala
+def isTruthy(a: Any) = {
+ if (a == 0 || a == "" || a == false)
+ false
+ else
+ true
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=method_16 %}
+
+```scala
+def isTruthy(a: Any) =
+ if a == 0 || a == "" || a == false then
+ false
+ else
+ true
+```
+
+{% endtab %}
+{% endtabs %}
+
+Примеры показывают, как работает метод:
+
+{% tabs method_17 %}
+{% tab 'Scala 2 и 3' for=method_17 %}
+
+```scala
+isTruthy(0) // false
+isTruthy("") // false
+isTruthy("hi") // true
+isTruthy(1.0) // true
+```
+
+{% endtab %}
+{% endtabs %}
+
+## Использование `match` в качестве тела метода
+
+Довольно часто в качестве тела метода используются `match`-выражения.
+Вот еще одна версия `isTruthy`, написанная с `match` выражением:
+
+{% tabs method_18 class=tabs-scala-version %}
+{% tab 'Scala 2' for=method_18 %}
+
+```scala
+def isTruthy(a: Any) = a match {
+ case 0 | "" | false => false
+ case _ => true
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=method_18 %}
+
+```scala
+def isTruthy(a: Matchable) = a match
+ case 0 | "" | false => false
+ case _ => true
+```
+
+> Этот метод работает точно так же, как и предыдущий, в котором использовалось выражение `if`/`else`.
+> Вместо `Any` в качестве типа параметра используется `Matchable`, чтобы принять любое значение,
+> поддерживающее сопоставление с образцом (pattern matching).
+
+> См. дополнительную информацию о trait `Matchable` в [Справочной документации][reference_matchable].
+
+[reference_matchable]: {{ site.scala3ref }}/other-new-features/matchable.html
+{% endtab %}
+{% endtabs %}
+
+## Контроль видимости методов в классах
+
+В классах, объектах, trait-ах и enum-ах методы Scala по умолчанию общедоступны,
+поэтому созданный здесь экземпляр `Dog` может получить доступ к методу `speak`:
+
+{% tabs method_19 class=tabs-scala-version %}
+{% tab 'Scala 2' for=method_19 %}
+
+```scala
+class Dog {
+ def speak() = println("Woof")
+}
+
+val d = new Dog
+d.speak() // печатает "Woof"
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=method_19 %}
+
+```scala
+class Dog:
+ def speak() = println("Woof")
+
+val d = new Dog
+d.speak() // печатает "Woof"
+```
+
+{% endtab %}
+{% endtabs %}
+
+Также методы можно помечать как `private`.
+Это делает их закрытыми в текущем классе, поэтому их нельзя вызвать или переопределить в подклассах:
+
+{% tabs method_20 class=tabs-scala-version %}
+{% tab 'Scala 2' for=method_20 %}
+```scala
+class Animal {
+ private def breathe() = println("I’m breathing")
+}
+
+class Cat extends Animal {
+ // этот метод не скомпилируется
+ override def breathe() = println("Yo, I’m totally breathing")
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=method_20 %}
+
+```scala
+class Animal:
+ private def breathe() = println("I’m breathing")
+
+class Cat extends Animal:
+ // этот метод не скомпилируется
+ override def breathe() = println("Yo, I’m totally breathing")
+```
+
+{% endtab %}
+{% endtabs %}
+
+Если необходимо сделать метод закрытым в текущем классе, но разрешить подклассам вызывать или переопределять его,
+метод помечается как `protected`, как показано в примере с методом `speak`:
+
+{% tabs method_21 class=tabs-scala-version %}
+{% tab 'Scala 2' for=method_21 %}
+
+```scala
+class Animal {
+ private def breathe() = println("I’m breathing")
+ def walk() = {
+ breathe()
+ println("I’m walking")
+ }
+ protected def speak() = println("Hello?")
+}
+
+class Cat extends Animal {
+ override def speak() = println("Meow")
+}
+
+val cat = new Cat
+cat.walk()
+cat.speak()
+cat.breathe() // не скомпилируется, потому что private
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=method_21 %}
+
+```scala
+class Animal:
+ private def breathe() = println("I’m breathing")
+ def walk() =
+ breathe()
+ println("I’m walking")
+ protected def speak() = println("Hello?")
+
+class Cat extends Animal:
+ override def speak() = println("Meow")
+
+val cat = new Cat
+cat.walk()
+cat.speak()
+cat.breathe() // не скомпилируется, потому что private
+```
+
+{% endtab %}
+{% endtabs %}
+
+Настройка `protected` означает:
+
+- к методу (или полю) могут обращаться другие экземпляры того же класса
+- метод (или поле) не виден в текущем пакете
+- он доступен для подклассов
+
+## Методы в объектах
+
+Ранее было показано, что trait-ы и классы могут иметь методы.
+Ключевое слово `object` используется для создания одноэлементного класса, и объект также может содержать методы.
+Это хороший способ сгруппировать набор “служебных” методов.
+Например, этот объект содержит набор методов, которые работают со строками:
+
+{% tabs method_22 class=tabs-scala-version %}
+{% tab 'Scala 2' for=method_22 %}
+
+```scala
+object StringUtils {
+
+ /**
+ * Returns a string that is the same as the input string, but
+ * truncated to the specified length.
+ */
+ def truncate(s: String, length: Int): String = s.take(length)
+
+ /**
+ * Returns true if the string contains only letters and numbers.
+ */
+ def lettersAndNumbersOnly_?(s: String): Boolean =
+ s.matches("[a-zA-Z0-9]+")
+
+ /**
+ * Returns true if the given string contains any whitespace
+ * at all. Assumes that `s` is not null.
+ */
+ def containsWhitespace(s: String): Boolean =
+ s.matches(".*\\s.*")
+
+}
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' for=method_22 %}
+
+```scala
+object StringUtils:
+
+ /**
+ * Returns a string that is the same as the input string, but
+ * truncated to the specified length.
+ */
+ def truncate(s: String, length: Int): String = s.take(length)
+
+ /**
+ * Returns true if the string contains only letters and numbers.
+ */
+ def lettersAndNumbersOnly_?(s: String): Boolean =
+ s.matches("[a-zA-Z0-9]+")
+
+ /**
+ * Returns true if the given string contains any whitespace
+ * at all. Assumes that `s` is not null.
+ */
+ def containsWhitespace(s: String): Boolean =
+ s.matches(".*\\s.*")
+
+end StringUtils
+```
+
+{% endtab %}
+{% endtabs %}
+
+## Методы расширения
+
+Есть много ситуаций, когда необходимо добавить функциональность к закрытым классам.
+Например, представьте, что у вас есть класс `Circle`, но вы не можете изменить его исходный код.
+Это может быть определено в сторонней библиотеке так:
+
+{% tabs method_23 %}
+{% tab 'Scala 2 и 3' for=method_23 %}
+
+```scala
+case class Circle(x: Double, y: Double, radius: Double)
+```
+
+{% endtab %}
+{% endtabs %}
+
+Если вы хотите добавить методы в этот класс, то можете определить их как методы расширения, например:
+
+{% tabs method_24 class=tabs-scala-version %}
+{% tab 'Scala 2' for=method_24 %}
+
+```scala
+implicit class CircleOps(c: Circle) {
+ def circumference: Double = c.radius * math.Pi * 2
+ def diameter: Double = c.radius * 2
+ def area: Double = math.Pi * c.radius * c.radius
+}
+```
+В Scala 2 используйте `implicit class`, подробности [здесь](/overviews/core/implicit-classes.html).
+
+{% endtab %}
+{% tab 'Scala 3' for=method_24 %}
+
+```scala
+extension (c: Circle)
+ def circumference: Double = c.radius * math.Pi * 2
+ def diameter: Double = c.radius * 2
+ def area: Double = math.Pi * c.radius * c.radius
+```
+В Scala 3 используйте новую конструкцию `extension`.
+Дополнительные сведения см. [в главах этой книги][extension] или [в справочнике по Scala 3][reference-ext].
+
+[reference-ext]: {{ site.scala3ref }}/contextual/extension-methods.html
+[extension]: {% link _overviews/scala3-book/ca-extension-methods.md %}
+{% endtab %}
+{% endtabs %}
+
+Теперь, когда у вас есть экземпляр `Circle` с именем `aCircle`,
+то вы можете вызывать эти методы следующим образом:
+
+{% tabs method_25 %}
+{% tab 'Scala 2 и 3' for=method_25 %}
+
+```scala
+aCircle.circumference
+aCircle.diameter
+aCircle.area
+```
+
+{% endtab %}
+{% endtabs %}
+
+## Дальнейшее изучение
+
+Есть много чего, что можно узнать о методах, в том числе:
+
+- Вызов методов в суперклассах
+- Определение и использование параметров по имени
+- Написание метода, который принимает параметр функции
+- Создание встроенных методов
+- Обработка исключений
+- Использование входных параметров vararg
+- Написание методов с несколькими группами параметров (частично применяемые функции).
+- Создание методов с параметрами универсального типа.
+
+Дополнительные сведения об этих функциях см. в других главах этой книги.
+
+[reference_extension_methods]: {{ site.scala3ref }}/contextual/extension-methods.html
+[reference_matchable]: {{ site.scala3ref }}/other-new-features/matchable.html
diff --git a/_ru/scala3/book/methods-summary.md b/_ru/scala3/book/methods-summary.md
new file mode 100644
index 0000000000..e19748a8fc
--- /dev/null
+++ b/_ru/scala3/book/methods-summary.md
@@ -0,0 +1,30 @@
+---
+layout: multipage-overview
+title: Обзор
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: section
+description: Эта страница подводит итог предыдущим разделам о методах в Scala 3.
+language: ru
+num: 26
+previous-page: methods-main-methods
+next-page: fun-intro
+---
+
+
+Есть ещё много чего, что можно узнать о методах, в том числе:
+
+- Вызов методов в суперклассах
+- Определение и использование параметров по имени
+- Создание метода, который принимает функцию в качестве параметра
+- Создание встроенных (_inline_) методов
+- Обработка исключений
+- Использование переменного числа входных параметров
+- Создание методов с несколькими группами параметров (частично применяемые функции)
+- Создание методов с параметрами универсального типа
+
+Дополнительные сведения об этих функциях доступны в [справочной документации][reference].
+
+
+[reference]: {{ site.scala3ref }}/overview.html
diff --git a/_ru/scala3/book/scala-features.md b/_ru/scala3/book/scala-features.md
index f1eab830aa..84dedb717f 100644
--- a/_ru/scala3/book/scala-features.md
+++ b/_ru/scala3/book/scala-features.md
@@ -91,7 +91,7 @@ val newNumbers = double(oldNumbers)
подобные этому, для вычисления того же результата:
{% tabs scala-features-2 %}
-{% tab 'Scala 2 and 3' for=scala-features-2 %}
+{% tab 'Scala 2 и 3' for=scala-features-2 %}
```scala
val newNumbers = oldNumbers.map(_ * 2)
```
@@ -108,7 +108,7 @@ Scala имеет краткий, удобочитаемый синтаксис.
Например, переменные создаются лаконично, а их типы понятны:
{% tabs scala-features-3 %}
-{% tab 'Scala 2 and 3' for=scala-features-3 %}
+{% tab 'Scala 2 и 3' for=scala-features-3 %}
```scala
val nums = List(1,2,3)
val p = Person("Martin", "Odersky")
@@ -120,7 +120,7 @@ val p = Person("Martin", "Odersky")
Функции высшего порядка и лямбда-выражения делают код кратким и удобочитаемым:
{% tabs scala-features-4 %}
-{% tab 'Scala 2 and 3' for=scala-features-4 %}
+{% tab 'Scala 2 и 3' for=scala-features-4 %}
```scala
nums.map(i => i * 2) // длинная форма
nums.map(_ * 2) // краткая форма
@@ -318,7 +318,7 @@ Scala 3 делает этот процесс более понятным, чем
Например, в Scala вы можете читать файлы с помощью `BufferedReader` и `FileReader` из Java:
{% tabs scala-features-7 %}
-{% tab 'Scala 2 and 3' for=scala-features-7 %}
+{% tab 'Scala 2 и 3' for=scala-features-7 %}
```scala
import java.io.*
val br = BufferedReader(FileReader(filename))
@@ -333,7 +333,7 @@ val br = BufferedReader(FileReader(filename))
то можете преобразовать их с помощью всего нескольких строк кода:
{% tabs scala-features-8 %}
-{% tab 'Scala 2 and 3' for=scala-features-8 %}
+{% tab 'Scala 2 и 3' for=scala-features-8 %}
```scala
import scala.jdk.CollectionConverters.*
val scalaList: Seq[Integer] = JavaClass.getJavaList().asScala.toSeq
diff --git a/_ru/scala3/book/taste-collections.md b/_ru/scala3/book/taste-collections.md
index 2585f24d20..672e5158af 100644
--- a/_ru/scala3/book/taste-collections.md
+++ b/_ru/scala3/book/taste-collections.md
@@ -23,7 +23,7 @@ next-page: taste-contextual-abstractions
В этих примерах показаны различные способы создания заполненного `List`:
{% tabs collection_1 %}
-{% tab 'Scala 2 and 3' for=collection_1 %}
+{% tab 'Scala 2 и 3' for=collection_1 %}
```scala
val a = List(1, 2, 3) // a: List[Int] = List(1, 2, 3)
@@ -48,7 +48,7 @@ val g = List.range(1, 10, 3) // g: List[Int] = List(1, 4, 7)
Результат, возвращаемый каждым выражением, отображается в комментарии к каждой строке:
{% tabs collection_2 %}
-{% tab 'Scala 2 and 3' for=collection_2 %}
+{% tab 'Scala 2 и 3' for=collection_2 %}
```scala
// a sample list
@@ -79,7 +79,7 @@ nums.flatMap(_.toUpperCase) // List('O', 'N', 'E', 'T', 'W', 'O')
для суммирования значений в последовательности целых чисел:
{% tabs collection_3 %}
-{% tab 'Scala 2 and 3' for=collection_3 %}
+{% tab 'Scala 2 и 3' for=collection_3 %}
```scala
val firstTen = (1 to 10).toList // List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
@@ -100,7 +100,7 @@ firstTen.foldLeft(100)(_ + _) // 155 (100 является “на
Например, используя данный case класс `Person`:
{% tabs collection_4 %}
-{% tab 'Scala 2 and 3' for=collection_4 %}
+{% tab 'Scala 2 и 3' for=collection_4 %}
```scala
case class Person(name: String)
@@ -112,7 +112,7 @@ case class Person(name: String)
Вот как вы создаете кортеж, который содержит `Int`, `String` и пользовательское значение `Person`:
{% tabs collection_5 %}
-{% tab 'Scala 2 and 3' for=collection_5 %}
+{% tab 'Scala 2 и 3' for=collection_5 %}
```scala
val t = (11, "eleven", Person("Eleven"))
@@ -125,7 +125,7 @@ val t = (11, "eleven", Person("Eleven"))
или получить к ним доступ по номеру:
{% tabs collection_6 %}
-{% tab 'Scala 2 and 3' for=collection_6 %}
+{% tab 'Scala 2 и 3' for=collection_6 %}
```scala
t(0) // 11
@@ -139,7 +139,7 @@ t(2) // Person("Eleven")
Вы также можете использовать этот метод _извлечения_, чтобы присвоить поля кортежа именам переменных:
{% tabs collection_7 %}
-{% tab 'Scala 2 and 3' for=collection_7 %}
+{% tab 'Scala 2 и 3' for=collection_7 %}
```scala
val (num, str, person) = t
diff --git a/_ru/scala3/book/taste-contextual-abstractions.md b/_ru/scala3/book/taste-contextual-abstractions.md
index 80d5e9e3a1..fea81bbb84 100644
--- a/_ru/scala3/book/taste-contextual-abstractions.md
+++ b/_ru/scala3/book/taste-contextual-abstractions.md
@@ -22,7 +22,7 @@ next-page: taste-toplevel-definitions
название города, а затем название улицы.
{% tabs contextual_1 %}
-{% tab 'Scala 2 and 3' for=contextual_1 %}
+{% tab 'Scala 2 и 3' for=contextual_1 %}
```scala
val addresses: List[Address] = ...
diff --git a/_ru/scala3/book/taste-functions.md b/_ru/scala3/book/taste-functions.md
index 207e154931..f83da448e7 100644
--- a/_ru/scala3/book/taste-functions.md
+++ b/_ru/scala3/book/taste-functions.md
@@ -27,7 +27,7 @@ Scala обладает большинством возможностей, кот
{% tabs function_1 %}
-{% tab 'Scala 2 and 3' for=function_1 %}
+{% tab 'Scala 2 и 3' for=function_1 %}
```scala
val a = List(1, 2, 3).map(i => i * 2) // List(2,4,6)
val b = List(1, 2, 3).map(_ * 2) // List(2,4,6)
@@ -39,7 +39,7 @@ val b = List(1, 2, 3).map(_ * 2) // List(2,4,6)
{% tabs function_2 %}
-{% tab 'Scala 2 and 3' for=function_2 %}
+{% tab 'Scala 2 и 3' for=function_2 %}
```scala
def double(i: Int): Int = i * 2
@@ -69,7 +69,7 @@ val b = List(1, 2, 3).map(double) // List(2,4,6)
{% tabs function_3 %}
-{% tab 'Scala 2 and 3' for=function_3 %}
+{% tab 'Scala 2 и 3' for=function_3 %}
```scala
// пример списка
val nums = (1 to 10).toList // List(1,2,3,4,5,6,7,8,9,10)
diff --git a/_ru/scala3/book/taste-hello-world.md b/_ru/scala3/book/taste-hello-world.md
index 29a59ae763..77442e43ca 100644
--- a/_ru/scala3/book/taste-hello-world.md
+++ b/_ru/scala3/book/taste-hello-world.md
@@ -109,7 +109,7 @@ Hello, World!
Чтобы использовать этот метод, вам нужно сначала его импортировать, например:
{% tabs import-readline %}
-{% tab 'Scala 2 and 3' for=import-readline %}
+{% tab 'Scala 2 и 3' for=import-readline %}
```scala
import scala.io.StdIn.readLine
```
@@ -190,7 +190,7 @@ Hello, Alvin Alexander!
если вы не используете подобное предложение `import`:
{% tabs import-readline-2 %}
-{% tab 'Scala 2 and 3' for=import-readline-2 %}
+{% tab 'Scala 2 и 3' for=import-readline-2 %}
```scala
import scala.io.StdIn.readLine
```
diff --git a/_ru/scala3/book/taste-methods.md b/_ru/scala3/book/taste-methods.md
index 87169991f4..c881761826 100644
--- a/_ru/scala3/book/taste-methods.md
+++ b/_ru/scala3/book/taste-methods.md
@@ -19,7 +19,7 @@ next-page: taste-functions
Синтаксис простого метода выглядит так:
{% tabs method_1 %}
-{% tab 'Scala 2 and 3' for=method_1 %}
+{% tab 'Scala 2 и 3' for=method_1 %}
```scala
def methodName(param1: Type1, param2: Type2): ReturnType =
// тело метода
@@ -31,7 +31,7 @@ def methodName(param1: Type1, param2: Type2): ReturnType =
Вот несколько примеров:
{% tabs method_2 %}
-{% tab 'Scala 2 and 3' for=method_2 %}
+{% tab 'Scala 2 и 3' for=method_2 %}
```scala
def sum(a: Int, b: Int): Int = a + b
def concatenate(s1: String, s2: String): String = s1 + s2
@@ -42,7 +42,7 @@ def concatenate(s1: String, s2: String): String = s1 + s2
Вам не нужно объявлять возвращаемый тип метода, поэтому можно написать эти методы следующим образом, если хотите:
{% tabs method_3 %}
-{% tab 'Scala 2 and 3' for=method_3 %}
+{% tab 'Scala 2 и 3' for=method_3 %}
```scala
def sum(a: Int, b: Int) = a + b
def concatenate(s1: String, s2: String) = s1 + s2
@@ -53,7 +53,7 @@ def concatenate(s1: String, s2: String) = s1 + s2
Вот как эти методы вызываются:
{% tabs method_4 %}
-{% tab 'Scala 2 and 3' for=method_4 %}
+{% tab 'Scala 2 и 3' for=method_4 %}
```scala
val x = sum(1, 2)
val y = concatenate("foo", "bar")
@@ -88,7 +88,7 @@ def getStackTraceAsString(t: Throwable): String =
В этом примере параметр `timeout` имеет значение по умолчанию `5000`:
{% tabs method_6 %}
-{% tab 'Scala 2 and 3' for=method_6 %}
+{% tab 'Scala 2 и 3' for=method_6 %}
```scala
def makeConnection(url: String, timeout: Int = 5000): Unit =
println(s"url=$url, timeout=$timeout")
@@ -99,7 +99,7 @@ def makeConnection(url: String, timeout: Int = 5000): Unit =
Поскольку в объявлении метода указано значение по умолчанию для `timeout`, метод можно вызывать двумя способами:
{% tabs method_7 %}
-{% tab 'Scala 2 and 3' for=method_7 %}
+{% tab 'Scala 2 и 3' for=method_7 %}
```scala
makeConnection("https://localhost") // url=http://localhost, timeout=5000
makeConnection("https://localhost", 2500) // url=http://localhost, timeout=2500
@@ -111,7 +111,7 @@ Scala также поддерживает использование _имено
поэтому вы можете вызвать этот метод, если хотите, вот так:
{% tabs method_8 %}
-{% tab 'Scala 2 and 3' for=method_8 %}
+{% tab 'Scala 2 и 3' for=method_8 %}
```scala
makeConnection(
url = "https://localhost",
@@ -126,7 +126,7 @@ makeConnection(
какие параметры установлены в `true` или `false`:
{% tabs method_9 %}
-{% tab 'Scala 2 and 3' for=method_9 %}
+{% tab 'Scala 2 и 3' for=method_9 %}
```scala
engage(true, true, true, false)
@@ -145,7 +145,7 @@ engage(true, true, true, false)
но, опуская эту деталь, примеры показывают, как работают методы расширения:
{% tabs extension %}
-{% tab 'Scala 3 Only' %}
+{% tab 'Только в Scala 3' %}
```scala
extension (s: String)
diff --git a/_ru/scala3/book/taste-modeling.md b/_ru/scala3/book/taste-modeling.md
index b50ac31aaf..deede29e6a 100644
--- a/_ru/scala3/book/taste-modeling.md
+++ b/_ru/scala3/book/taste-modeling.md
@@ -388,7 +388,7 @@ enum Nat:
Этот код демонстрирует несколько функций `case class`:
{% tabs case-class %}
-{% tab 'Scala 2 and 3' for=case-class %}
+{% tab 'Scala 2 и 3' for=case-class %}
```scala
// определение case class
diff --git a/_ru/scala3/book/taste-objects.md b/_ru/scala3/book/taste-objects.md
index 14c2d79229..a244c7cfa2 100644
--- a/_ru/scala3/book/taste-objects.md
+++ b/_ru/scala3/book/taste-objects.md
@@ -53,7 +53,7 @@ object StringUtils:
Поскольку `StringUtils` - это "одиночка", его методы можно вызывать непосредственно для объекта:
{% tabs object_2 %}
-{% tab 'Scala 2 and 3' for=object_2 %}
+{% tab 'Scala 2 и 3' for=object_2 %}
```scala
val x = StringUtils.isNullOrEmpty("") // true
val x = StringUtils.isNullOrEmpty("a") // false
diff --git a/_ru/scala3/book/taste-repl.md b/_ru/scala3/book/taste-repl.md
index 3d35bb9d55..e45dc0e8cc 100644
--- a/_ru/scala3/book/taste-repl.md
+++ b/_ru/scala3/book/taste-repl.md
@@ -45,7 +45,7 @@ REPL — это интерпретатор командной строки, по
Теперь можно вводить выражения Scala, чтобы увидеть, как они работают:
{% tabs expression-one %}
-{% tab 'Scala 2 and 3' for=expression-one %}
+{% tab 'Scala 2 и 3' for=expression-one %}
````
scala> 1 + 1
val res0: Int = 2
@@ -61,7 +61,7 @@ REPL автоматически создает для вас переменны
Эти имена переменных можно использовать в последующих выражениях:
{% tabs expression-two %}
-{% tab 'Scala 2 and 3' for=expression-two %}
+{% tab 'Scala 2 и 3' for=expression-two %}
````
scala> val x = res0 * 10
val x: Int = 20
@@ -75,7 +75,7 @@ val x: Int = 20
В этом примере показано, как создать, а затем вызвать метод `sum`:
{% tabs expression-three %}
-{% tab 'Scala 2 and 3' for=expression-three %}
+{% tab 'Scala 2 и 3' for=expression-three %}
````
scala> def sum(a: Int, b: Int): Int = a + b
def sum(a: Int, b: Int): Int
diff --git a/_ru/scala3/book/taste-toplevel-definitions.md b/_ru/scala3/book/taste-toplevel-definitions.md
index c0bac88150..3d15f774a8 100644
--- a/_ru/scala3/book/taste-toplevel-definitions.md
+++ b/_ru/scala3/book/taste-toplevel-definitions.md
@@ -17,7 +17,7 @@ next-page: taste-summary
Например, вы можете создать файл с именем _MyCoolApp.scala_ и поместить в него следующее содержимое:
{% tabs toplevel_1 %}
-{% tab 'Scala 3 only' for=toplevel_1 %}
+{% tab 'Только в Scala 3' for=toplevel_1 %}
```scala
import scala.collection.mutable.ArrayBuffer
@@ -57,7 +57,7 @@ type Money = BigDecimal
как в этом примере:
{% tabs toplevel_2 %}
-{% tab 'Scala 3 only' for=toplevel_2 %}
+{% tab 'Только в Scala 3' for=toplevel_2 %}
```scala
package foo {
def double(i: Int) = i * 2
diff --git a/_ru/scala3/book/taste-vars-data-types.md b/_ru/scala3/book/taste-vars-data-types.md
index ab848966d0..7409db07b4 100644
--- a/_ru/scala3/book/taste-vars-data-types.md
+++ b/_ru/scala3/book/taste-vars-data-types.md
@@ -41,7 +41,7 @@ next-page: taste-control-structures
Эти примеры показывают, как создавать `val` и `var` переменные:
{% tabs var-express-1 %}
-{% tab 'Scala 2 and 3' %}
+{% tab 'Scala 2 и 3' %}
```scala
// неизменяемая
@@ -57,7 +57,7 @@ var b = 1
Появится ошибка компилятора, если попытаться её изменить:
{% tabs var-express-2 %}
-{% tab 'Scala 2 and 3' %}
+{% tab 'Scala 2 и 3' %}
```scala
val msg = "Hello, world"
@@ -69,7 +69,7 @@ msg = "Aloha" // ошибка "reassignment to val"; этот код не ск
И наоборот, `var` можно переназначить:
{% tabs var-express-3 %}
-{% tab 'Scala 2 and 3' %}
+{% tab 'Scala 2 и 3' %}
```scala
var msg = "Hello, world"
@@ -83,7 +83,7 @@ msg = "Aloha" // этот код скомпилируется, потому ч
Когда вы создаете переменную, то можете явно объявить ее тип или позволить компилятору его вывести:
{% tabs var-express-4 %}
-{% tab 'Scala 2 and 3' %}
+{% tab 'Scala 2 и 3' %}
```scala
val x: Int = 1 // явно
@@ -96,7 +96,7 @@ val x = 1 // неявно; компилятор выводит тип
Компилятор Scala обычно может определить тип данных за вас, как показано в выводе этих примеров REPL:
{% tabs var-express-5 %}
-{% tab 'Scala 2 and 3' %}
+{% tab 'Scala 2 и 3' %}
```scala
scala> val x = 1
@@ -115,7 +115,7 @@ val nums: List[Int] = List(1, 2, 3)
но в простых присваиваниях, подобных нижеследующим, в этом нет необходимости:
{% tabs var-express-6 %}
-{% tab 'Scala 2 and 3' %}
+{% tab 'Scala 2 и 3' %}
```scala
val x: Int = 1
@@ -136,7 +136,7 @@ Scala поставляется со стандартными числовыми
Эти примеры показывают, как объявлять переменные числовых типов:
{% tabs var-express-7 %}
-{% tab 'Scala 2 and 3' %}
+{% tab 'Scala 2 и 3' %}
```scala
val b: Byte = 1
@@ -152,7 +152,7 @@ val f: Float = 3.0
Поскольку `Int` и `Double` являются числовыми типами по умолчанию, то обычно они создаются без явного объявления типа:
{% tabs var-express-8 %}
-{% tab 'Scala 2 and 3' %}
+{% tab 'Scala 2 и 3' %}
```scala
val i = 123 // по умолчанию Int
@@ -165,7 +165,7 @@ val j = 1.0 // по умолчанию Double
чтобы указать, что они являются `Long`, `Double` или `Float` значениями:
{% tabs var-express-9 %}
-{% tab 'Scala 2 and 3' %}
+{% tab 'Scala 2 и 3' %}
```scala
val x = 1_000L // val x: Long = 1000
@@ -178,7 +178,7 @@ val z = 3.3F // val z: Float = 3.3
Когда вам нужны действительно большие числа, используйте типы `BigInt` и `BigDecimal`:
{% tabs var-express-10 %}
-{% tab 'Scala 2 and 3' %}
+{% tab 'Scala 2 и 3' %}
```scala
var a = BigInt(1_234_567_890_987_654_321L)
@@ -192,7 +192,7 @@ var b = BigDecimal(123_456.789)
В Scala также есть типы данных `String` и `Char`:
{% tabs var-express-11 %}
-{% tab 'Scala 2 and 3' %}
+{% tab 'Scala 2 и 3' %}
```scala
val name = "Bill" // String
@@ -214,7 +214,7 @@ val c = 'a' // Char
Например, учитывая эти три переменные:
{% tabs var-express-12 %}
-{% tab 'Scala 2 and 3' %}
+{% tab 'Scala 2 и 3' %}
```scala
val firstName = "John"
@@ -227,7 +227,7 @@ val lastName = "Doe"
Вы можете объединить эти переменные в строку следующим образом:
{% tabs var-express-13 %}
-{% tab 'Scala 2 and 3' %}
+{% tab 'Scala 2 и 3' %}
```scala
println(s"Name: $firstName $mi $lastName") // "Name: John C Doe"
@@ -240,7 +240,7 @@ println(s"Name: $firstName $mi $lastName") // "Name: John C Doe"
Чтобы вставить произвольные выражения в строку, заключите их в фигурные скобки:
{% tabs var-express-14 %}
-{% tab 'Scala 2 and 3' %}
+{% tab 'Scala 2 и 3' %}
``` scala
println(s"2 + 2 = ${2 + 2}") // prints "2 + 2 = 4"
@@ -261,7 +261,7 @@ println(s"x.abs = ${x.abs}") // prints "x.abs = 1"
Многострочные строки создаются путем включения строки в три двойные кавычки:
{% tabs var-express-15 %}
-{% tab 'Scala 2 and 3' %}
+{% tab 'Scala 2 и 3' %}
```scala
val quote = """The essence of Scala:
diff --git a/_ru/scala3/book/why-scala-3.md b/_ru/scala3/book/why-scala-3.md
index 3a8efd51f3..02e260d668 100644
--- a/_ru/scala3/book/why-scala-3.md
+++ b/_ru/scala3/book/why-scala-3.md
@@ -39,7 +39,7 @@ next-page: taste-intro
Например, `List` определяется как класс---технически это абстрактный класс---и новый экземпляр создается следующим образом:
{% tabs list %}
-{% tab 'Scala 2 and 3' for=list %}
+{% tab 'Scala 2 и 3' for=list %}
```scala
val x = List(1, 2, 3)
```
@@ -54,7 +54,7 @@ val x = List(1, 2, 3)
`List` API также состоит из десятков других методов, многие из которых являются функциями высшего порядка:
{% tabs list-methods %}
-{% tab 'Scala 2 and 3' for=list-methods %}
+{% tab 'Scala 2 и 3' for=list-methods %}
```scala
val xs = List(1, 2, 3, 4, 5)
@@ -75,7 +75,7 @@ _Вывод типов_ (_type inference_) в Scala часто заставля
Это верно для объявления переменной:
{% tabs dynamic %}
-{% tab 'Scala 2 and 3' for=dynamic %}
+{% tab 'Scala 2 и 3' for=dynamic %}
```scala
val a = 1
val b = "Hello, world"
@@ -88,7 +88,7 @@ val stuff = ("fish", 42, 1_234.5)
Это также верно при передаче анонимных функций функциям высшего порядка:
{% tabs dynamic-hof %}
-{% tab 'Scala 2 and 3' for=dynamic-hof %}
+{% tab 'Scala 2 и 3' for=dynamic-hof %}
```scala
list.filter(_ < 4)
list.map(_ * 2)
@@ -101,7 +101,7 @@ list.filter(_ < 4)
и при определении методов:
{% tabs dynamic-method %}
-{% tab 'Scala 2 and 3' for=dynamic-method %}
+{% tab 'Scala 2 и 3' for=dynamic-method %}
```scala
def add(a: Int, b: Int) = a + b
```
@@ -111,7 +111,7 @@ def add(a: Int, b: Int) = a + b
Это как никогда верно для Scala 3, например, при использовании [типов объединения][union-types]:
{% tabs union %}
-{% tab 'Scala 3 Only' for=union %}
+{% tab 'Только в Scala 3' for=union %}
```scala
// параметр типа объединения
def help(id: Username | Password) =
@@ -131,7 +131,7 @@ val b: Password | Username = if (true) name else password
Scala — это неформальный, “краткий, но все же читабельный“ язык. Например, объявление переменной лаконично:
{% tabs concise %}
-{% tab 'Scala 2 and 3' for=concise %}
+{% tab 'Scala 2 и 3' for=concise %}
```scala
val a = 1
val b = "Hello, world"
@@ -143,7 +143,7 @@ val c = List(1,2,3)
Создание типов, таких как трейты, классы и перечисления, является кратким:
{% tabs enum %}
-{% tab 'Scala 3 Only' for=enum %}
+{% tab 'Только в Scala 3' for=enum %}
```scala
trait Tail:
def wagTail(): Unit
@@ -166,7 +166,7 @@ case class Person(
Функции высшего порядка кратки:
{% tabs list-hof %}
-{% tab 'Scala 2 and 3' for=list-hof %}
+{% tab 'Scala 2 и 3' for=list-hof %}
```scala
list.filter(_ < 4)
@@ -262,7 +262,7 @@ Scala также можно использовать в браузере с [п
Вот некоторые примеры:
{% tabs list-more %}
-{% tab 'Scala 2 and 3' for=list-more %}
+{% tab 'Scala 2 и 3' for=list-more %}
```scala
List.range(1, 3) // List(1, 2)
List.range(start = 1, end = 6, step = 2) // List(1, 3, 5)
@@ -310,7 +310,7 @@ nums.sortWith(_ > _) // List(10, 8, 7, 5, 1)
Для неизменяемости рекомендуется создавать неизменяемые val переменные:
{% tabs val %}
-{% tab 'Scala 2 and 3' for=val %}
+{% tab 'Scala 2 и 3' for=val %}
```scala
val a = 1 // неизменяемая переменная
```
@@ -320,7 +320,7 @@ val a = 1 // неизменяемая переменная
Вам также рекомендуется использовать неизменяемые классы коллекций, такие как `List` и `Map`:
{% tabs list-map %}
-{% tab 'Scala 2 and 3' for=list-map %}
+{% tab 'Scala 2 и 3' for=list-map %}
```scala
val b = List(1,2,3) // List неизменяем
val c = Map(1 -> "one") // Map неизменяема
@@ -331,7 +331,7 @@ val c = Map(1 -> "one") // Map неизменяема
Case классы в первую очередь предназначены для использования в [моделировании предметной области]({% link _overviews/scala3-book/domain-modeling-intro.md %}), и их параметры также неизменяемы:
{% tabs case-class %}
-{% tab 'Scala 2 and 3' for=case-class %}
+{% tab 'Scala 2 и 3' for=case-class %}
```scala
case class Person(name: String)
val p = Person("Michael Scott")
@@ -345,7 +345,7 @@ p.name = "Joe" // compiler error (переназначение val name)
и вы можете передавать в них методы (не показаны) и анонимные функции:
{% tabs higher-order %}
-{% tab 'Scala 2 and 3' for=higher-order %}
+{% tab 'Scala 2 и 3' for=higher-order %}
```scala
a.dropWhile(_ < 25)
a.filter(_ < 25)
@@ -444,7 +444,7 @@ _Безопасность_ связана с несколькими новыми
добавленные в Scala 3 довольно удобочитаемым образом:
{% tabs extension %}
-{% tab 'Scala 3 Only' for=extension %}
+{% tab 'Только в Scala 3' for=extension %}
```scala
// перечисления
enum Color: