Что такое «чистые» функции в программировании?

Ответ

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

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

Пример чистой функции в PHP:

// Чистая функция
function calculateDiscount(float $price, float $discountPercent): float {
    return $price * (1 - $discountPercent / 100);
}

// Использование
$finalPrice = calculateDiscount(1000, 15); // Всегда 850
$finalPrice2 = calculateDiscount(1000, 15); // Снова 850 - детерминировано

Пример НЕчистой функции:

// Нечистая функция - зависит от внешнего состояния
$taxRate = 20;

function calculatePriceWithTax(float $price): float {
    global $taxRate; // Зависимость от глобальной переменной
    $taxRate += 1; // Побочный эффект - изменение внешнего состояния
    return $price * (1 + $taxRate / 100);
}

// Результат зависит от порядка вызовов
$price1 = calculatePriceWithTax(1000); // 1210 (если taxRate был 20)
$price2 = calculatePriceWithTax(1000); // 1221 - разный результат!

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

1. Предсказуемость и надежность:

// Легко рассуждать о поведении
$result = calculateDiscount(1000, 10);
// Гарантированно вернет 900, независимо от контекста

2. Простота тестирования:

// Не нужны моки и сложная настройка окружения
public function testCalculateDiscount(): void
{
    $this->assertEquals(850, calculateDiscount(1000, 15));
    $this->assertEquals(0, calculateDiscount(0, 50));
    $this->assertEquals(1000, calculateDiscount(1000, 0));
}

3. Возможность кэширования (мемоизация):

// Реализация простого кэша для чистой функции
function memoizedCalculateDiscount(float $price, float $discountPercent): float {
    static $cache = [];
    $key = $price . '|' . $discountPercent;

    if (!isset($cache[$key])) {
        $cache[$key] = $price * (1 - $discountPercent / 100);
    }

    return $cache[$key];
}

4. Параллельное выполнение:

// Чистые функции можно безопасно выполнять параллельно
$prices = [100, 200, 300, 400];
$discountedPrices = array_map(
    fn($price) => calculateDiscount($price, 20),
    $prices
);
// Можно распараллелить без риска гонок данных

5. Референциальная прозрачность:

// Вызов функции можно заменить ее результатом
$total = calculateDiscount(1000, 10) + calculateDiscount(500, 10);
// Эквивалентно:
$total = 900 + 450; // 1350

Практическое применение в PHP:

  • Функции-хелперы для работы с данными (форматирование, валидация)
  • Бизнес-логика в Domain слое (DDD)
  • Функциональные преобразования в коллекциях
  • Сервисные классы без состояния

Ограничения: Не все можно сделать чистыми функциями — работа с БД, файлами, внешними API по определению имеет побочные эффекты. Ключ в изоляции "нечистых" операций и максимизации использования чистых функций для бизнес-логики.

Ответ 18+ 🔞

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

Чистые функции (pure functions) — это, по сути, такие функции-отличницы, которые всегда ведут себя прилично. У них две главные фишки:

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

Вот тебе пример чистой функции на PHP, чтобы было понятно:

// Чистая функция — святая корова
function calculateDiscount(float $price, float $discountPercent): float {
    return $price * (1 - $discountPercent / 100);
}

// Использование
$finalPrice = calculateDiscount(1000, 15); // Всегда 850, ёпта
$finalPrice2 = calculateDiscount(1000, 15); // Опять 850, хоть сто раз вызывай

А вот пример функции, которая ведёт себя как последняя мразота:

// Нечистая функция — настоящий распиздяй
$taxRate = 20;

function calculatePriceWithTax(float $price): float {
    global $taxRate; // Лезет в глобальную переменную, как в чужой холодильник
    $taxRate += 1; // Побочный эффект — меняет что-то снаружи, ядрёна вошь!
    return $price * (1 + $taxRate / 100);
}

// Результат теперь зависит от того, в каком порядке ты её дёргаешь
$price1 = calculatePriceWithTax(1000); // Допустим, 1210
$price2 = calculatePriceWithTax(1000); // А тут уже 1221 — волнение ебать! Где логика?

Так зачем, спрашивается, этот цирк с чистыми функциями? А вот зачем, чувак:

1. Предсказуемость и надёжность — доверия ебать ноль к тем, кто их не использует.

// Не надо гадать на кофейной гуще
$result = calculateDiscount(1000, 10);
// Гарантированно вернёт 900, даже если у тебя сервер горит. Как швейцарские часы.

2. Простота тестирования — не надо городить огород из моков и заглушек.

// Тест пишется за две секунды
public function testCalculateDiscount(): void
{
    $this->assertEquals(850, calculateDiscount(1000, 15));
    $this->assertEquals(0, calculateDiscount(0, 50));
    $this->assertEquals(1000, calculateDiscount(1000, 0));
}
// Никаких подвохов, всё прозрачно, как слёза ребёнка.

3. Возможность кэширования (мемоизация) — чтобы не вычислять одно и то же сто раз.

// Простой кэш — и производительность взлетает
function memoizedCalculateDiscount(float $price, float $discountPercent): float {
    static $cache = [];
    $key = $price . '|' . $discountPercent;

    if (!isset($cache[$key])) {
        $cache[$key] = $price * (1 - $discountPercent / 100);
    }

    return $cache[$key];
}
// Первый раз посчитал, запомнил, дальше просто отдаёт из кэша. Умно, блядь.

4. Параллельное выполнение — можно кидать задачи в разные потоки без страха, что они друг другу нахуй наступят.

$prices = [100, 200, 300, 400];
$discountedPrices = array_map(
    fn($price) => calculateDiscount($price, 20),
    $prices
);
// Запускай хоть в десяти потоках — гонки данных не будет, потому что функции не лезут в общее состояние.

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

$total = calculateDiscount(1000, 10) + calculateDiscount(500, 10);
// По сути, то же самое, что:
$total = 900 + 450; // 1350
// Мозг не взрывается, когда пытаешься понять логику.

Где это применить в реальной жизни на PHP?

  • Различные хелперы для форматирования данных или валидации.
  • Ядро бизнес-логики в Domain слое (это если ты по DDD шаришь).
  • Преобразования коллекций в стиле функционального программирования.
  • Любые сервисы, которые не тащат за собой состояние, как хвост.

Но есть и ограничения, куда без них. Нельзя же всё сделать чистым — работа с базой данных, файлами или внешними API по своей природе грешна и имеет побочные эффекты. Весь фокус в том, чтобы изолировать эту "грязь" и максимально использовать чистые функции для всей логики, которая может без этого обойтись. Тогда код будет жить долго и счастливо, а не разваливаться при первом чихе.