Программирование на языке Scala/Чистые функции
Определения
правитьПобочный эффект
правитьЧистая функция
правитьЧистая функция (Pure Function) — это функция, которые зависят только от своих параметров и не имеют побочных эффектов. С одними и теми же аргументами они всегда выдают одно возвращаемое значение. Можно называть их математическими функциями.
Сылочная прозрачность
правитьДействие
правитьФункция с побочными эффектами (actions — A)
Вычисление
правитьЧистая функция (calculations — C)
Данные
править(data - D)
Чистая функция (Pure Function) - это функция, которая:
- Всегда возвращает одинаковый результат для одних и тех же входных данных (принцип детерминизма). Это означает, что вызов функции с одними и теми же аргументами всегда вернет одинаковый результат.
- Не имеет побочных эффектов. Под побочными эффектами понимаются изменения состояния программы, которые видны снаружи функции. Это включает в себя изменение глобальных переменных, вывод в консоль, запись в файл, изменение структур данных и т. д.
Чистые функции обладают несколькими важными свойствами:
- Они предсказуемы и надежны, так как их поведение зависит только от входных данных, и это поведение не может быть нарушено внешними факторами.
- Чистые функции более легко тестировать, так как для тестов можно использовать фиксированные входные данные, а результат всегда ожидаем.
- Они упрощают композицию функции, то есть вы можете комбинировать чистые функции в более сложные функции без опасения косвенных взаимосвязей.
- Они могут быть легко оптимизированы и кэшированы, так как результат зависит только от входных данных.
В функциональном программировании чистые функции считаются ключевой концепцией, и максимальное использование чистых функций способствует созданию более надежных и поддерживаемых программных систем.
Полностью программу сделать из чистых функции невозможно. Если у вас есть программа, полностью состоящая из чистых функций, то она не сможет взаимодействовать с внешним миром. Такая программа бесполезна. У вас должна быть возможность отправлять запросы к сервисам или регистрировать сообщения в консоли. Цель функционального программирования состоит в том, чтобы составить большую часть программы из чистых функций. Побочные эффекты неизбежны, но, ограничив их определенными местами в вашем приложении, ими будет легче управлять и отслеживать.
Предсказуемость
правитьОсновным преимуществом написания небольших чистых функций, вероятно, является их предсказуемость. В связи с тем, что ваша функция не обращается к внешним переменным и не изменяет ничего за пределами своей области видимости, вы можете легко предсказать ее вывод в зависимости от ввода. Более того можно с легкотью, даже не исследуя реализацию функции, понять что она делает, основываясь только на названии, и входных и выходных типах данных.
Тестирование
правитьИспользуя чистые функции, вы избегаете каких-либо дерзких побочных эффектов, и ваша функция не получает доступа и не изменяет что-либо из глобальной области. Это снимает бремя сложнного тестового окружения. Что нужно вашей функции, передается в качестве входных данных, и вам нужно будет только проверить правильность вывода. Тестировать такие функции намного проще. Когда строительные блоки вашего приложения являются самодостаточными и не делают ничего необычного, вам просто нужно убедиться, что все так, как должно быть.
Композиция
правитьКомпозиция чистых функций создает чистую функцию.
Ссылочная прозрачность
правитьСсылочная прозрачность (referential transparency) - это свойство функции, также известное как "заменяемость" или "идентичность замены". Оно означает, что для конкретных входных значений, функцию можно заменить ее выходными значениями, при этом итоговый результат программы не измениться.
Оптимизация
правитьКеширование
правитьЕсли чистая функция выполняет тяжелые вычисления, можно сохранять результаты для определенных аргументов и повторно использовать эти результаты сразу, при повторных вызовах, без устаревания этих результатов.
Оптимизация использования
правитьЕсли результат чистой функции в логической цепи не используется, то и выполнения такая функция не требует, так как ничего кроме выходного значения такая функция не дает. Компилятор может предотвратить вызов такой функции. Также можно отложить время выполнения чистой функции, до тех пор пока её значение будет действительно восстребавано, а не в месте указанным кодом. Так как она не производит побочных эфектов, логика программы не будет нарушена.
Параллельные вычисления
правитьЧистую фукцию обычно легко разложить на несколько частей и выполнять их параллельно.
Рефакторинг
правитьЧистую функцию намного проще модифицировать.
Признаки нечистоты функции
правитьПрисутствие в описании типа функции Unit
правитьЕсли в выходном значении присутствует тип Unit (пустое значение), то однозначно можно сказать, что данная функция нечистая.
Если входные аргументы отсутствуют, то скорее всего данная функция нечистая. Чистая функция без аргументов должна выдавать константное значение, и в использовании функции нет смысла.
Неполное количество аргументов функции
правитьНе все используемые значения в теле функции описаны как аргументы функции. Это означает, что функция использует внешние значения, которые могут быть изменены, что приведет к изменению работы функции.
Использование нечистых функций
правитьЕсли в теле функции используется нечистая функция, то и использующая её функция становится нечистой. Нечистая функция "заражает" свой нечистотой другие функции. Поэтому необходимо "изолировать" нечистые функции, и использовать их только в специально отведенных зонах кода.
Модификация входящих структур данных
правитьМодификация значения структуры данных, вместо создания новой копии, тоже является нечистой операцией, так как используемые методы модификации являются нечистыми. Однако, модификация структуры данных созданной внутри фукции не делает её нечистой, так как такая структура еще не доступна другим функциям.
Функция умеет выводить свой результат способом или типом не описанным как выходной результат
правитьЕсть различные приемы или случаи, когда функция помимо заявленного выходного значения может давать незаявленное значение или неявный эффект. Такими значениями могут быть бросаемые функцией исключения (специальные экстренные значения при различных сбоях, с которыми ознакомимся позже) или неявные эффекты связанные с аппаратным обеспечением. Такие результаты работы фунции трудно определимы по телу функции, и если они заложены преднамеренно, должны быть явно описаны.
Однако, каждая функция выполняемая на компьютере, может по независящим от её кода причинам выкинуть исключение или прервать свое выполнение, что теоретически делает каждую функцию нечистой.
Чистата в функциях высшего порядка
правитьЧистата функции высшего порядка не является однозначной, так как это зависит от используемых ею функций, которые заранее не определены, а присуствуют в виде функциональных входных типов.
Однозначно можно сказать то функция высшего порядка нечистая, если в описании типа входящих функций присутcвует Unit, или она явно использует нечистую функцию.
Популярность
правитьВ стандартной библиотеке Scala, а также в других важных библиотеках чистые методы и функции являются доминирующими.
Домашнее задание
править1. В учебном проекте создать файл PureFunction.sc (Scala-Worksheet)
2. Является ли функция fun1 чистой?
val fun1: () => String =
() => "Hello, world!"
3. Является ли функция fun2 чистой?
val fun2: () => Unit =
() => println("Hello, world!")
4. Является ли функция fun3 чистой?
val fun3: () => String =
() => random().toString
5. Является ли функция fun4 чистой?
val a = 2
val fun4: Int => Int =
b => a + b
6. Является ли функция fun5 чистой?
val a = fun3().toInt
val fun5: Int => Int =
b => a + b
7. Является ли функция fun6 чистой?
val fun6: Int => Int =
b => fun3().toInt + b
8. Является ли функция fun7 чистой?
val fun7: (Int, Int) => Int =
(a, b) => a / b
9. Является ли функция fun8 чистой?
val fun8: String => String =
a => fun1() + a
10. Является ли функция fun9 чистой?
val fun9: (() => Int, Int) => Int =
(fun, b) => fun() + b