Что такое чистая функция (pure function)?

Ответ

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

  1. Детерминированность (Deterministic): Для одних и тех же входных аргументов всегда возвращает одинаковый результат.
  2. Отсутствие побочных эффектов (No Side Effects): Не изменяет внешнее состояние (глобальные переменные, параметры-ссылки, файловую систему, БД) и не зависит от изменяемого внешнего состояния.

Примеры:

// ЧИСТАЯ функция
public static int Add(int a, int b) => a + b;

public static string Greet(string name) => $"Hello, {name}!";

// НЕЧИСТАЯ функция (зависит от внешнего, изменяемого состояния)
private static int _counter = 0;
public static int GetNextId() => ++_counter; // Побочный эффект: изменяет _counter

// НЕЧИСТАЯ функция (результат недетерминирован)
public static int GetRandom() => new Random().Next(); // Разный результат при каждом вызове

// НЕЧИСТАЯ функция (выполняет ввод-вывод)
public static void Log(string message) => File.AppendAllText("log.txt", message);

Почему это важно? Преимущества чистых функций:

  • Тестируемость: Легко проверить, не требуя сложной настройки окружения (mocks, stubs).
  • Предсказуемость и надежность: Поведение функции изолировано и понятно.
  • Возможности для оптимизации: Кэширование результатов (мемоизация), параллельное выполнение.
  • Упрощение рассуждений о коде: Функцию можно анализировать и понимать в отрыве от контекста выполнения.

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

Ответ 18+ 🔞

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

Представь себе, есть у тебя функция. Идеальная, блядь, функция. Как швейцарские часы. Ты ей даёшь одни и те же цифры — она тебе один и тот же результат выплёвывает. Никаких сюрпризов. Это и есть детерминированность, ёпта. Сложил 2 и 2? Получи 4. И завтра, и через год, и на продакшене, и у тебя на ноуте в сортире. Всегда 4. Не "иногда 4, а иногда 5, потому что глобальная переменная _mood устала".

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

Вот смотри, на C#:

// Это святое, это чистая функция. Как монах в келье.
public static int Add(int a, int b) => a + b;
public static string Greet(string name) => $"Hello, {name}!";

Дал Greet("Вася") — получил "Hello, Вася!". И точка. Никаких танцев с бубном.

А теперь смотри на этого уёбка:

private static int _counter = 0;
public static int GetNextId() => ++_counter; // Побочный эффект: изменяет _counter

Он тебе каждый раз разный результат выдаёт! И главное — он гадит в общее поле _counter. Вызвал его десять раз — он там наворотил, а потом другой модуль смотрит на этот счётчик и охуевает: "А почему у меня тут 10, я же только один раз запросил?". Пиздец, а не функция.

Или вот этот шут:

public static int GetRandom() => new Random().Next(); // Разный результат при каждом вызове

Ну это ваще пиздец. Детерминированность? Да ни хуя подобного! Сегодня 42, завтра 15. На тестах пролетает, на проде — нет. Классика.

А этот вообще, блядь, в файловую систему лезет:

public static void Log(string message) => File.AppendAllText("log.txt", message);

Это не функция, это уже процедура с побочкой. Вызвал — и пошла жара: диск скрипит, права проверяются, место кончается. Чистота? Да какая нахуй чистота!

А зачем это всё, спросишь? Да затем, чувак!

  • Тестировать их — одно удовольствие. Не надо поднимать целое окружение, мокать базу или файловую систему. Подсунул входные данные — проверил выход. Всё. Быстро и без нервов.
  • Предсказуемость. Ты точно знаешь, что она делает. Не зависит от погоды, фазы луны или значения какой-то переменной в другом потоке. Спать спокойно можно.
  • Оптимизация. Раз результат для одних и тех же аргументов всегда одинаковый, его можно закэшировать и не пересчитывать каждый раз. Это называется мемоизация. Или можно несколько таких функций параллельно запустить — они друг другу мешать не будут, ибо ничего общего не трогают.
  • Понимание кода. Читаешь такую функцию — и всё ясно. Не надо бегать по всему проекту и искать, кто ещё меняет состояние, от которого она зависит. Всё у неё внутри аргументов.

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

Видео-ответы