0 added
0 removed
Original
2026-01-01
Modified
2026-03-10
1
<p>Сегодня многие приложения пишутся с учётом концепции ООП и используют императивный код. Но несмотря на широкую популярность ООП в современной разработке, такой подход имеет ряд недостатков: 1. В большинстве высокоуровневых языков обработка ошибок реализована посредством механизма исключений. Сама идея прерывания выполнения при некорректном состоянии хороша, но реализация в виде выброса исключения с последующим его перехватом в другом месте требует от команды разработчиков железной дисциплины, иначе простая линейная вычислительная цепочка превратится в древообразную или даже в запутанный граф. А вот в функциональной парадигме предоставляются абстракции для прозрачной и простой обработки ошибок. 2. Императивный код хорош для описания последовательных вычислений, но сегодня большинство приложений асинхронны и многопоточны, поэтому уследить за всем сложно. Вспоминаем ситуацию, когда 2 потока пытаются изменить одно и то же значение либо первый поток ждёт второй поток, когда второй, в свою очередь, первый. 3. Отсутствует математически строгая, аксиоматизированная и пригодная к повседневному использованию теория, описывающая систему типов и паттерны проектирования. Да, сами паттерны проектирования являются неплохим шагом в сторону стандартизации способов проектирования, но их по большему счёту можно скорее назвать сборником указаний и советов, нежели строгой теории. А комбинируя паттерны, вы не сможете со 100%-ной уверенностью сказать, что в итоге получится.</p>
1
<p>Сегодня многие приложения пишутся с учётом концепции ООП и используют императивный код. Но несмотря на широкую популярность ООП в современной разработке, такой подход имеет ряд недостатков: 1. В большинстве высокоуровневых языков обработка ошибок реализована посредством механизма исключений. Сама идея прерывания выполнения при некорректном состоянии хороша, но реализация в виде выброса исключения с последующим его перехватом в другом месте требует от команды разработчиков железной дисциплины, иначе простая линейная вычислительная цепочка превратится в древообразную или даже в запутанный граф. А вот в функциональной парадигме предоставляются абстракции для прозрачной и простой обработки ошибок. 2. Императивный код хорош для описания последовательных вычислений, но сегодня большинство приложений асинхронны и многопоточны, поэтому уследить за всем сложно. Вспоминаем ситуацию, когда 2 потока пытаются изменить одно и то же значение либо первый поток ждёт второй поток, когда второй, в свою очередь, первый. 3. Отсутствует математически строгая, аксиоматизированная и пригодная к повседневному использованию теория, описывающая систему типов и паттерны проектирования. Да, сами паттерны проектирования являются неплохим шагом в сторону стандартизации способов проектирования, но их по большему счёту можно скорее назвать сборником указаний и советов, нежели строгой теории. А комбинируя паттерны, вы не сможете со 100%-ной уверенностью сказать, что в итоге получится.</p>
2
<h2>А что насчёт функционального подхода?</h2>
2
<h2>А что насчёт функционального подхода?</h2>
3
<p>В функциональном программировании подошли с другой стороны. К примеру, отказались от возможности менять переменные настолько, насколько это возможно. Функция с точки зрения информатики - не равна функции с точки зрения математики.Та же функция датчика случайных чисел не является математической функцией, ведь она не принимает параметров, в результате чего с точки зрения математики она должна быть константой, но каждый раз такая функция выдаёт разный результат, чего в математике не бывает. Если же запретить переменным меняться (уничтожить глобальное изменяемое состояние), функции станут действительно математическими, поэтому можно будет построить строгую математическую теорию. Как раз такая теория и была построена и сегодня она называется<strong>λ-исчислениями</strong>. Такая теория описывает типы и полиморфизм.</p>
3
<p>В функциональном программировании подошли с другой стороны. К примеру, отказались от возможности менять переменные настолько, насколько это возможно. Функция с точки зрения информатики - не равна функции с точки зрения математики.Та же функция датчика случайных чисел не является математической функцией, ведь она не принимает параметров, в результате чего с точки зрения математики она должна быть константой, но каждый раз такая функция выдаёт разный результат, чего в математике не бывает. Если же запретить переменным меняться (уничтожить глобальное изменяемое состояние), функции станут действительно математическими, поэтому можно будет построить строгую математическую теорию. Как раз такая теория и была построена и сегодня она называется<strong>λ-исчислениями</strong>. Такая теория описывает типы и полиморфизм.</p>
4
<p>Математики-топологи создали довольно абстрактную теория категорий, которую они применили для сокращения доказательств. Постепенно теория категорий нашла применение во многих отраслях математики. Информатики выяснили, что такая теория подходит при описании операций с реальными данными, которые не всегда бывают корректны. К примеру, понятие монады можно использовать при описании вычислений, способных привести к ошибке.</p>
4
<p>Математики-топологи создали довольно абстрактную теория категорий, которую они применили для сокращения доказательств. Постепенно теория категорий нашла применение во многих отраслях математики. Информатики выяснили, что такая теория подходит при описании операций с реальными данными, которые не всегда бывают корректны. К примеру, понятие монады можно использовать при описании вычислений, способных привести к ошибке.</p>
5
<p>Сами монады бывают 2-х видов. Первый - "чистая" монада, представляющая корректные данные. Второй- некорректная монада, необходимая для представления некорректных данных. Над этими 2-мя типами данных определены следующие операции: 1.<strong>pure</strong>- функция для создания "чистой" монады. 2.<strong>flatMap</strong>- использование преобразования, которое порой приводит к некорректному результату. Допустим, операция деления целых чисел корректно обрабатывает все данные за исключением деления на 0. В таком случае создаётся некорректная монада и её не изменит применение к ней операции flatMap. 3.<strong>map</strong>- применение к данным операции, которая не способна вызвать исключений по своей природе, допустим, операции сложения.</p>
5
<p>Сами монады бывают 2-х видов. Первый - "чистая" монада, представляющая корректные данные. Второй- некорректная монада, необходимая для представления некорректных данных. Над этими 2-мя типами данных определены следующие операции: 1.<strong>pure</strong>- функция для создания "чистой" монады. 2.<strong>flatMap</strong>- использование преобразования, которое порой приводит к некорректному результату. Допустим, операция деления целых чисел корректно обрабатывает все данные за исключением деления на 0. В таком случае создаётся некорректная монада и её не изменит применение к ней операции flatMap. 3.<strong>map</strong>- применение к данным операции, которая не способна вызвать исключений по своей природе, допустим, операции сложения.</p>
6
<p>Следует отметить, что функциональное программирование находится посередине между низкоуровневыми операциями и высокими уровнями абстракции, такими как уровень приложений либо сервисов. Как раз с учётом данных обстоятельств и создали<strong>объектно-функциональный стиль</strong>, который позволяет применять императивный код для низкоуровневых функций, чистый функциональный код в середине приложения и, соответственно, паттерны проектирования и другие ООП-технологии на самом высоком уровне абстракции. Из этого следует, что функциональный подход лучше использовать не для создания замкнутых систем, а в большей степени для описания бизнес-логики и создания слоёв приложений. И именно в этом ключе функциональное программирование используется сегодня - оно построено внутри объектных абстракций над низкоуровневым императивным кодом.</p>
6
<p>Следует отметить, что функциональное программирование находится посередине между низкоуровневыми операциями и высокими уровнями абстракции, такими как уровень приложений либо сервисов. Как раз с учётом данных обстоятельств и создали<strong>объектно-функциональный стиль</strong>, который позволяет применять императивный код для низкоуровневых функций, чистый функциональный код в середине приложения и, соответственно, паттерны проектирования и другие ООП-технологии на самом высоком уровне абстракции. Из этого следует, что функциональный подход лучше использовать не для создания замкнутых систем, а в большей степени для описания бизнес-логики и создания слоёв приложений. И именно в этом ключе функциональное программирование используется сегодня - оно построено внутри объектных абстракций над низкоуровневым императивным кодом.</p>
7
<p>Чтобы показать, зачем нужна та либо другая функциональная конструкция, начнём с небольшого введения в мультипарадигменный язык программирования, имеющий мощную поддержку функционального подхода -<strong>Scala</strong>.</p>
7
<p>Чтобы показать, зачем нужна та либо другая функциональная конструкция, начнём с небольшого введения в мультипарадигменный язык программирования, имеющий мощную поддержку функционального подхода -<strong>Scala</strong>.</p>
8
<p>Статья, которую вы сейчас читаете, выйдет в двух частях и опишет фундаментальные языковые конструкции, а также некоторые средства сокращения кода.</p>
8
<p>Статья, которую вы сейчас читаете, выйдет в двух частях и опишет фундаментальные языковые конструкции, а также некоторые средства сокращения кода.</p>
9
<h2>Переходим к Scala</h2>
9
<h2>Переходим к Scala</h2>
10
<p>Первая версия Scala вышла в 2004 г. на платформе Java. Нынешняя версия языка имеет версии для JavaScript (Scala-js) и LLVM (Scala Native).</p>
10
<p>Первая версия Scala вышла в 2004 г. на платформе Java. Нынешняя версия языка имеет версии для JavaScript (Scala-js) и LLVM (Scala Native).</p>
11
<p>Поначалу, многие реализованные решения были созданы с целью устранить такие недостатки Java, как отсутствие синтаксического сахара и слабая типобезопасность. При этом новый язык сохранил возможность почти бесшовной интеграции с Java, то есть вы сможете использовать Java-библиотеки без ущерба для производительности.</p>
11
<p>Поначалу, многие реализованные решения были созданы с целью устранить такие недостатки Java, как отсутствие синтаксического сахара и слабая типобезопасность. При этом новый язык сохранил возможность почти бесшовной интеграции с Java, то есть вы сможете использовать Java-библиотеки без ущерба для производительности.</p>
12
<p>Крупные корпорации оценили язык по достоинству: Scala активно применяется в LinkedIn, Netflix, Twitter, Sony и пр.</p>
12
<p>Крупные корпорации оценили язык по достоинству: Scala активно применяется в LinkedIn, Netflix, Twitter, Sony и пр.</p>
13
<h2>Устанавливаем IDE и SBT</h2>
13
<h2>Устанавливаем IDE и SBT</h2>
14
<p>Для использования Scala под платформу Java, потребуются установленные JRE и JDK. При этом для знакомства с языком целесообразно применять IntelliJ IDEA CommunityEdition со спецплагином ScalaPlugin (он доступен при установке). Также пригодится система построения проектов SBT (есть под разные операционные системы и скачивается при создании проекта).</p>
14
<p>Для использования Scala под платформу Java, потребуются установленные JRE и JDK. При этом для знакомства с языком целесообразно применять IntelliJ IDEA CommunityEdition со спецплагином ScalaPlugin (он доступен при установке). Также пригодится система построения проектов SBT (есть под разные операционные системы и скачивается при создании проекта).</p>
15
<p>Далее в статье последуют примеры, для запуска которых вполне подойдёт online-компилятор<a>Scalastie</a>.</p>
15
<p>Далее в статье последуют примеры, для запуска которых вполне подойдёт online-компилятор<a>Scalastie</a>.</p>
16
<p>Итак, чтобы создать первый проект, делаем следующее:</p>
16
<p>Итак, чтобы создать первый проект, делаем следующее:</p>
17
new project → scala → sbt<p>Далее в меню создания проекта выбираем версию SBT и Scala. После того, как вы нажмёте кнопку создания проекта, IDEA отдаст соответствующее указание о создании SBT-проекта (об этом свидетельствует надпись dump project structurefrom SBT), в результате чего структура папок появится не сразу - имейте это в виду.</p>
17
new project → scala → sbt<p>Далее в меню создания проекта выбираем версию SBT и Scala. После того, как вы нажмёте кнопку создания проекта, IDEA отдаст соответствующее указание о создании SBT-проекта (об этом свидетельствует надпись dump project structurefrom SBT), в результате чего структура папок появится не сразу - имейте это в виду.</p>
18
<p>После формирования проекта вы сможете увидеть приблизительно следующую структуру папок:</p>
18
<p>После формирования проекта вы сможете увидеть приблизительно следующую структуру папок:</p>
19
<p>Естественно, наибольший интерес представляет папка src, а также её подпапки, плюс файл build.sbt. Этот файл описывает структуру проекта и применяется для определения модулей и подмодулей, управления версиями библиотек и зависимостями. Сюда же дописываются импорты сторонних библиотек.</p>
19
<p>Естественно, наибольший интерес представляет папка src, а также её подпапки, плюс файл build.sbt. Этот файл описывает структуру проекта и применяется для определения модулей и подмодулей, управления версиями библиотек и зависимостями. Сюда же дописываются импорты сторонних библиотек.</p>
20
<p>Итак, в папке main/scala создаём newscalaclass и выбираем там object. Его имя должно совпадать с именем файла, где он расположен, в обратном случае возможны ошибки. Создав object, увидите:</p>
20
<p>Итак, в папке main/scala создаём newscalaclass и выбираем там object. Его имя должно совпадать с именем файла, где он расположен, в обратном случае возможны ошибки. Создав object, увидите:</p>
21
<p>Следующим шагом создаём в теле object точку входа в программу - метод main() (в языке программирования Scala методы объявляются ключевым словом def). Как только начнёте набирать defmain, автодополнение IDEA предложит сгенерировать метод. Итоговый код будет следующим:</p>
21
<p>Следующим шагом создаём в теле object точку входа в программу - метод main() (в языке программирования Scala методы объявляются ключевым словом def). Как только начнёте набирать defmain, автодополнение IDEA предложит сгенерировать метод. Итоговый код будет следующим:</p>
22
object HelloWorld { def main(args: Array[String]): Unit = { } }<p>Если параметры командной строки args не нужны, код можно сократить:</p>
22
object HelloWorld { def main(args: Array[String]): Unit = { } }<p>Если параметры командной строки args не нужны, код можно сократить:</p>
23
object HelloWorld { def main: Unit = { } }<p>Тут есть ряд особенностей: - в списке параметров сначала идут имена этих параметров, а потом типы параметров после двоеточия; - тип возвращаемого значения указывают через двоеточие после списка параметров (в языке программирования Scala все функции всегда возвращают значение, но если надобности в этом нет, применяется тип Unit - аналог ключевого слова void, который указывает на отсутствие какого-нибудь возвращаемого значения).</p>
23
object HelloWorld { def main: Unit = { } }<p>Тут есть ряд особенностей: - в списке параметров сначала идут имена этих параметров, а потом типы параметров после двоеточия; - тип возвращаемого значения указывают через двоеточие после списка параметров (в языке программирования Scala все функции всегда возвращают значение, но если надобности в этом нет, применяется тип Unit - аналог ключевого слова void, который указывает на отсутствие какого-нибудь возвращаемого значения).</p>
24
<p>После добавления строчки println("Helloworld"), вы можете запустить программу (нажмите зелёный треугольник напротив main). В консоли появится классическая строка "Hello world".</p>
24
<p>После добавления строчки println("Helloworld"), вы можете запустить программу (нажмите зелёный треугольник напротив main). В консоли появится классическая строка "Hello world".</p>
25
object HelloWorld extends App{ println("Hello world") }<p>Если вы любите писать код в чистых текстовых редакторах, то в этом случае всё же не стоит пренебрегать IDEA. Дело в том, что в Scala есть много хитрых конструкций, а у IDEA - большое количество весьма полезных языко-специфических функций. Благодаря этому, среда разработки убережёт вас от ряда глупых ошибок в процессе написания кода.</p>
25
object HelloWorld extends App{ println("Hello world") }<p>Если вы любите писать код в чистых текстовых редакторах, то в этом случае всё же не стоит пренебрегать IDEA. Дело в том, что в Scala есть много хитрых конструкций, а у IDEA - большое количество весьма полезных языко-специфических функций. Благодаря этому, среда разработки убережёт вас от ряда глупых ошибок в процессе написания кода.</p>
26
<h2>Переменные в Scala</h2>
26
<h2>Переменные в Scala</h2>
27
<p>В Scala есть два типа переменных - val и var.</p>
27
<p>В Scala есть два типа переменных - val и var.</p>
28
<p>Val является неизменяемой переменной, которой вы можете присвоить значение лишь при инициализации. А вот как выглядит синтаксис введения постоянной:</p>
28
<p>Val является неизменяемой переменной, которой вы можете присвоить значение лишь при инициализации. А вот как выглядит синтаксис введения постоянной:</p>
29
val valueName: TypeName = value<p>Как и у параметров функции, сначала располагается имя постоянной, а после двоеточия - имя типа. В языке есть довольно продвинутая система типов, поэтому во многих случаях вы сможете опускать имя типа, а компилятор выведет его автоматически (IDEA это тоже умеет, достаточно поставить курсор на имя переменной и нажатьCtrl+Q).</p>
29
val valueName: TypeName = value<p>Как и у параметров функции, сначала располагается имя постоянной, а после двоеточия - имя типа. В языке есть довольно продвинутая система типов, поэтому во многих случаях вы сможете опускать имя типа, а компилятор выведет его автоматически (IDEA это тоже умеет, достаточно поставить курсор на имя переменной и нажатьCtrl+Q).</p>
30
<p>Чтобы объявить переменную, используйте ключевое слово var:</p>
30
<p>Чтобы объявить переменную, используйте ключевое слово var:</p>
31
var valueName: TypeName = value<p>Как и в случае с постоянной, тип во многих случаях можно опускать. Что касается переменных, то их можно объединять в кортежи, используя удобный синтаксис запаковки в кортеж:</p>
31
var valueName: TypeName = value<p>Как и в случае с постоянной, тип во многих случаях можно опускать. Что касается переменных, то их можно объединять в кортежи, используя удобный синтаксис запаковки в кортеж:</p>
32
val tuple: (Type1, Type2, ... , TypeN) = (val1, val2, … , valN )<p>Распаковка из кортежа:</p>
32
val tuple: (Type1, Type2, ... , TypeN) = (val1, val2, … , valN )<p>Распаковка из кортежа:</p>
33
val (val1: Type1, … valN: TypeN) = tuple<p>Кстати, типы тоже можете опускать, сокращая код:</p>
33
val (val1: Type1, … valN: TypeN) = tuple<p>Кстати, типы тоже можете опускать, сокращая код:</p>
34
val tuple = (val1, val2, … , valN ) val (val1, … val2) = tuple<h2>Методы в Scala</h2>
34
val tuple = (val1, val2, … , valN ) val (val1, … val2) = tuple<h2>Методы в Scala</h2>
35
<p>Чтобы определить методы, используем ключевое слово def. Синтаксис следующий:</p>
35
<p>Чтобы определить методы, используем ключевое слово def. Синтаксис следующий:</p>
36
def methodName (parameter1: Type1, parameter2: Type2, … , parameterN: TypeN): returnType = { // method body }<p>Обратите внимание, что в определении нет слова return. Дело в том, что последнее значение в функции является возвращаемым. Аналогично и с оператором if: вопреки логике,которая принята в императивных языках, оператор if (condition) value_if_true else value_if_false имеет возвращаемое значение с типом, общим над value_if_true/value_if_false и значение, в зависимости от условия равное value_if_true/value_if_false.</p>
36
def methodName (parameter1: Type1, parameter2: Type2, … , parameterN: TypeN): returnType = { // method body }<p>Обратите внимание, что в определении нет слова return. Дело в том, что последнее значение в функции является возвращаемым. Аналогично и с оператором if: вопреки логике,которая принята в императивных языках, оператор if (condition) value_if_true else value_if_false имеет возвращаемое значение с типом, общим над value_if_true/value_if_false и значение, в зависимости от условия равное value_if_true/value_if_false.</p>
37
<p>Есть в функциях и синтаксический сахар. К примеру, если функция параметров не принимает, скобки можно не писать. Также можно не указывать тип возвращаемого значения. Как и в случаях с переменными, тип будет выведен (в среде разработки IDEA данный тип напечатается фантомным текстом). Очередная "сахарная" особенность заключается в том, что когда тело метода является коротким и содержит всего одну инструкцию, фигурных скобок можно не писать. Вот корректный код:</p>
37
<p>Есть в функциях и синтаксический сахар. К примеру, если функция параметров не принимает, скобки можно не писать. Также можно не указывать тип возвращаемого значения. Как и в случаях с переменными, тип будет выведен (в среде разработки IDEA данный тип напечатается фантомным текстом). Очередная "сахарная" особенность заключается в том, что когда тело метода является коротким и содержит всего одну инструкцию, фигурных скобок можно не писать. Вот корректный код:</p>
38
<p>Следует отметить, что функция может быть значением, при этом она будет иметь функциональный тип, записываемый как SourceType =>ResultType.</p>
38
<p>Следует отметить, что функция может быть значением, при этом она будет иметь функциональный тип, записываемый как SourceType =>ResultType.</p>
39
<p>Допустим, функция, увеличивающая число на 1 (здесь num + 1 - возвращаемое значение, а Int -тип):</p>
39
<p>Допустим, функция, увеличивающая число на 1 (здесь num + 1 - возвращаемое значение, а Int -тип):</p>
40
def inc (num: Int) = num +1<p>будет иметь тип Int =>Int.</p>
40
def inc (num: Int) = num +1<p>будет иметь тип Int =>Int.</p>
41
<p>В этом случае мы сможем ввести постоянные, а их значениями станут наши функции:</p>
41
<p>В этом случае мы сможем ввести постоянные, а их значениями станут наши функции:</p>
42
val incFunc: Int=>Int = inc<p>Какова разница между val и def? Она заключается в том, что val вычисляется однажды и потом применяется при каждом упоминании, а значение типа def при упоминании вычисляется каждый раз. Это можно проверить следующим кодом:</p>
42
val incFunc: Int=>Int = inc<p>Какова разница между val и def? Она заключается в том, что val вычисляется однажды и потом применяется при каждом упоминании, а значение типа def при упоминании вычисляется каждый раз. Это можно проверить следующим кодом:</p>
43
val valrand = Random.nextInt() def defrand = Random.nextInt() println(valrand) println(valrand) println(defrand) println(defrand)<p>В нашем случае первые 2 строчки вывода будут совпадать, а вторые 2 будут различаться.</p>
43
val valrand = Random.nextInt() def defrand = Random.nextInt() println(valrand) println(valrand) println(defrand) println(defrand)<p>В нашем случае первые 2 строчки вывода будут совпадать, а вторые 2 будут различаться.</p>
44
<p>Когда же у функции входных параметров много, тип запишется следующим образом:</p>
44
<p>Когда же у функции входных параметров много, тип запишется следующим образом:</p>
45
(Type1, Type2, … TypeN) =>ResultType<p>Давайте посмотрим на функцию сложения 2-х чисел:</p>
45
(Type1, Type2, … TypeN) =>ResultType<p>Давайте посмотрим на функцию сложения 2-х чисел:</p>
46
def add(a: Int, b: Int) = a+b<p>Она будет иметь тип: (Int, Int) =>Int.</p>
46
def add(a: Int, b: Int) = a+b<p>Она будет иметь тип: (Int, Int) =>Int.</p>
47
<p>Когда же нам потребуется вернуть несколько значений из функции, можно воспользоваться распаковкой и упаковкой в кортеж.</p>
47
<p>Когда же нам потребуется вернуть несколько значений из функции, можно воспользоваться распаковкой и упаковкой в кортеж.</p>
48
<p>Есть для функций и синтаксический сахар, позволяющий создавать функцию без ввода для неё отдельного метода:</p>
48
<p>Есть для функций и синтаксический сахар, позволяющий создавать функцию без ввода для неё отдельного метода:</p>
49
<p>При этом value вполне себе может быть блоком, где можно вводить дополнительные переменные, совершая некоторые действия:</p>
49
<p>При этом value вполне себе может быть блоком, где можно вводить дополнительные переменные, совершая некоторые действия:</p>
50
val sum: (Int, Int) => Int = (a,b) => a+b val f2: Int => Int = a =>{ val v1 = Random.nextInt() val v2 = v1 % 10 -5 a - v2 }<p>Так как для функций есть тип, имеется возможность создания функции от функций, т. е. функции высшего порядка - здесь достаточно лишь дописать параметру функциональный тип. Например, мы можем написать функцию, которая к 2-м числам применяет заданное преобразование:</p>
50
val sum: (Int, Int) => Int = (a,b) => a+b val f2: Int => Int = a =>{ val v1 = Random.nextInt() val v2 = v1 % 10 -5 a - v2 }<p>Так как для функций есть тип, имеется возможность создания функции от функций, т. е. функции высшего порядка - здесь достаточно лишь дописать параметру функциональный тип. Например, мы можем написать функцию, которая к 2-м числам применяет заданное преобразование:</p>
51
def transform (a: Int, b: Int, f: (int, Int) => Int): Int = f(a,b)<p>Существует ещё одна особенность, связанная с функциями: она заключается в наличии двух семантик передачи параметров: call by name и call by value. Последняя семантика используется по умолчанию: сначала вычисляется значение, а потом оно передаётся в функцию. Что касается Call by name, то тут, напротив, значение сначала передаётся в функцию, а потом вычисляется в каждом месте его упоминания. Если надо указать эту семантику, следует перед типом поставить знак =>:</p>
51
def transform (a: Int, b: Int, f: (int, Int) => Int): Int = f(a,b)<p>Существует ещё одна особенность, связанная с функциями: она заключается в наличии двух семантик передачи параметров: call by name и call by value. Последняя семантика используется по умолчанию: сначала вычисляется значение, а потом оно передаётся в функцию. Что касается Call by name, то тут, напротив, значение сначала передаётся в функцию, а потом вычисляется в каждом месте его упоминания. Если надо указать эту семантику, следует перед типом поставить знак =>:</p>
52
var i =0 def inc = { i += 1 i } def callByValueDemonstration (num: Int) = { println(num) println(num) println(num) } def callByNameDemonstration (num: =>Int) = { println(num) println(num) println(num) } callByValueDemonstration(inc) callByNameDemonstration(inc)<p>В итоге вы должны получить вывод следующего содержания: 1 1 1 2 3 4. Это значит, что если вы желаете передать в функцию генератор случайных чисел, следует задействовать семантику callByName.</p>
52
var i =0 def inc = { i += 1 i } def callByValueDemonstration (num: Int) = { println(num) println(num) println(num) } def callByNameDemonstration (num: =>Int) = { println(num) println(num) println(num) } callByValueDemonstration(inc) callByNameDemonstration(inc)<p>В итоге вы должны получить вывод следующего содержания: 1 1 1 2 3 4. Это значит, что если вы желаете передать в функцию генератор случайных чисел, следует задействовать семантику callByName.</p>
53
<h2>Вместо заключения</h2>
53
<h2>Вместо заключения</h2>
54
<p>Это вводная часть статьи про функциональное программирование на Scala. В ней мы ознакомились с основами Scala: рассмотрели окружение, особенности ввода переменных и функций, а также 2 вида семантики методов - callByName и callByValue.</p>
54
<p>Это вводная часть статьи про функциональное программирование на Scala. В ней мы ознакомились с основами Scala: рассмотрели окружение, особенности ввода переменных и функций, а также 2 вида семантики методов - callByName и callByValue.</p>
55
<p>В следующий раз поговорим про объектную модель Scala и особенности, касающиеся синтаксического сахара.</p>
55
<p>В следующий раз поговорим про объектную модель Scala и особенности, касающиеся синтаксического сахара.</p>
56
<p><em>Материал написан на основании статьи Ивана Камышана<a>"Основы функционального программирования с примерами на Scala - часть 1"</a>.</em></p>
56
<p><em>Материал написан на основании статьи Ивана Камышана<a>"Основы функционального программирования с примерами на Scala - часть 1"</a>.</em></p>
57
57