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

January 12, 2021

Начало темы здесь

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

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

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

  1. У неё нет результата (возвращает void/Unit). Теоретически можно сделать такую чистую функцию, но пользы от неё будет очень мало - её можно будет использовать только в качестве затычки для какого-то вывода.

    Типичный пример - printf/println.

  2. У неё нет аргументов. Опять же, теоретически можно сделать такую функцию, но пользы от неё будет очень мало - её можно будет использовать только в качестве затычки для какого-то ввода.

    Типичный пример - currentTime.

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

    Типичный пример - Files.readString(Path.of(``/tmp/test’’))

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

    Типичные примеры: кэширование детерминированных функций и логгирование в детерминированных функциях.

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

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

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

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

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

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

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

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

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

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

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

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

Кэширование

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

Оптимизация

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

Бесплатный контракт

Чистые функции бесплатно дают контракт.


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