Перейти к содержанию

Программирование на языке Scala/Чистые функции

Материал из Викиверситета

Определения

[править]

Побочный эффект

[править]

Чистая функция

[править]

Чистая функция (Pure Function) - это функция, которые зависят только от своих параметров и не имеют побочных эффектов. С одними и теми же аргументами они всегда выдают одно возвращаемое значение. Можно называть их математическими функциями.

Сылочная прозрачность

[править]

Действие

[править]

Функция с побочными эффектами (actions — A)

Вычисление

[править]

Чистая функция (calculations — C)

Данные

[править]

(data - D)


Чистая функция (Pure Function) - это функция, которая:

  1. Всегда возвращает одинаковый результат для одних и тех же входных данных (принцип детерминизма). Это означает, что вызов функции с одними и теми же аргументами всегда вернет одинаковый результат.
  2. Не имеет побочных эффектов. Под побочными эффектами понимаются изменения состояния программы, которые видны снаружи функции. Это включает в себя изменение глобальных переменных, вывод в консоль, запись в файл, изменение структур данных и т. д.

Чистые функции обладают несколькими важными свойствами:

  • Они предсказуемы и надежны, так как их поведение зависит только от входных данных, и это поведение не может быть нарушено внешними факторами.
  • Чистые функции более легко тестировать, так как для тестов можно использовать фиксированные входные данные, а результат всегда ожидаем.
  • Они упрощают композицию функции, то есть вы можете комбинировать чистые функции в более сложные функции без опасения косвенных взаимосвязей.
  • Они могут быть легко оптимизированы и кэшированы, так как результат зависит только от входных данных.

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

Полностью программу сделать из чистых функции невозможно. Если у вас есть программа, полностью состоящая из чистых функций, то она не сможет взаимодействовать с внешним миром. Такая программа бесполезна. У вас должна быть возможность отправлять запросы к сервисам или регистрировать сообщения в консоли. Цель функционального программирования состоит в том, чтобы составить большую часть программы из чистых функций. Побочные эффекты неизбежны, но, ограничив их определенными местами в вашем приложении, ими будет легче управлять и отслеживать.

Предсказуемость

[править]

Основным преимуществом написания небольших чистых функций, вероятно, является их предсказуемость. В связи с тем, что ваша функция не обращается к внешним переменным и не изменяет ничего за пределами своей области видимости, вы можете легко предсказать ее вывод в зависимости от ввода. Более того можно с легкотью, даже не исследуя реализацию функции, понять что она делает, основываясь только на названии, и входных и выходных типах данных.

Тестирование

[править]

Используя чистые функции, вы избегаете каких-либо дерзких побочных эффектов, и ваша функция не получает доступа и не изменяет что-либо из глобальной области. Это снимает бремя сложнного тестового окружения. Что нужно вашей функции, передается в качестве входных данных, и вам нужно будет только проверить правильность вывода. Тестировать такие функции намного проще. Когда строительные блоки вашего приложения являются самодостаточными и не делают ничего необычного, вам просто нужно убедиться, что все так, как должно быть.

Композиция

[править]

Композиция чистых функций создает чистую функцию.

Ссылочная прозрачность

[править]

Ссылочная прозрачность (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