Чистые и грязные функции, эффекты и обработка сигналов, сайдэффекты: чистые функции

Чистые и грязные функции, эффекты и обработка сигналов, сайдэффекты: чистые функции

Алексей

Привет!

Обращаю ваше внимание, что топик расширился - помимо анонсированных вчера грязных функций, вчера же я ещё "открыл" сигналы.
Заголовок конечно длинноват, но пока так:)

Но обо всём по порядку и сегодня у нас чистые функции.


Чистая функция - это функция без эффектов, сайдэффектов и обработки сигналов:)

Или функция в математическом смысле - её результат определяется исключительно параметрами и у неё только один результат - возвращаемое значение.


Признаки не чистоты функций:

  1. У неё нет результата (возвращает void/Unit).
    Теоретически можно сделать такую чистую функцию, но пользы от неё будет очень мало - её можно будет использовать только в качестве затычки для какого-то вывода.
    Типичный пример - printf/println.
  2. У неё нет аргументов.
    Опять же, теоретически можно сделать такую функцию, но пользы от неё будет очень мало - её можно будет использовать только в качестве затычки для какого-то ввода.
    Типичный пример - currentTime
  3. Будучи вызванной с одними параметрами в разное время, она может вернуть разные результаты.
    Тут уже без вариантов, по определению все такие функции не являются чистыми
    Типичный пример - Files.readString(Path.of("/tmp/test"))
  4. Будучи детерминированной, она обладает наблюдаемым эффектом, помимо возврата значения.
    Опять же без вариантов, все такие функции не чистые по определению.
    Типичные примеры: кэширование детерминированных функций и логгирование в детерминированных функциях.


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


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


Модульность чтения

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

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

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

Безопасность рефакторинга

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

Ну ток что компилятор откажется компилировать.

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

Чистые функции тестируются тривиально - готовите параметры, вызываете, проверяете результат вызова.

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

Композиция/переиспользование

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

Например, функцию валидации, которая кроме возврата результата валидации ещё и сохраняет запрос на валидацию в БД, врядли удастся использовать в каком-то новом контексте.

Параллельное исполнение

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

Кэширование

С чистыми функциями инвалидация кэша больше не является проблемой.

Оптимизация

Пруфы что это реально используется искать лень, но теоретически компиляторы могут оптимизировать чистые функции намного более агрессивно за счёт безопасности рефакторинга.




И раз чистые функции столь прекрасны, почему все не пишут только чистые прекрасные функции?
Потому что мы создаём программы ради их эффектов на наш физический мир.
Продолжение следует:)