Функциональное программирование в PHP: продвинутый уровень

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Функциональное программирование в PHP: продвинутый уровень

Функциональное программирование (ФП) — это парадигма, в которой вычисления рассматриваются как вычисление математических функций, а изменение состояния и изменяемые данные избегаются. Хотя PHP изначально не был спроектирован как чисто функциональный язык, начиная с версии 7.4 и особенно в PHP 8.x, он получил мощные инструменты для написания кода в функциональном стиле. В этой статье мы разберем продвинутые концепции ФП в PHP: от функций высшего порядка до монад, с практическими примерами и рекомендациями.

1. Функции высшего порядка и замыкания

Функции высшего порядка — это функции, которые принимают другие функции в качестве аргументов или возвращают их. В PHP это реализуется через анонимные функции (замыкания) и встроенные функции array_map, array_filter, array_reduce.

Пример: функция обратного вызова для фильтрации

<?php
$numbers = [1, 2, 3, 4, 5, 6];

// Функция высшего порядка
$filterEven = function(array $items, callable $callback): array {
    $result = [];
    foreach ($items as $item) {
        if ($callback($item)) {
            $result[] = $item;
        }
    }
    return $result;
};

// Анонимная функция-предикат
$isEven = fn($n) => $n % 2 === 0;

$evenNumbers = $filterEven($numbers, $isEven);
// Результат: [2, 4, 6]
print_r($evenNumbers);
?>

Пример: фабрика функций (замыкание с состоянием)

<?php
function createMultiplier(float $factor): callable {
    // Замыкание "запоминает" $factor
    return fn($number) => $number * $factor;
}

$double = createMultiplier(2);
$triple = createMultiplier(3);

echo $double(5); // 10
echo $triple(5); // 15
?>

Замыкания в PHP позволяют захватывать переменные из внешней области видимости через use (в PHP 7.4+ можно использовать стрелочные функции fn). Это основа для построения каррирования и частичного применения.

2. Каррирование и частичное применение

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

Пример: ручное каррирование

<?php
// Обычная функция
function add(int $a, int $b): int {
    return $a + $b;
}

// Каррированная версия
$curriedAdd = function(int $a): callable {
    return function(int $b) use ($a): int {
        return $a + $b;
    };
};

$addFive = $curriedAdd(5);
echo $addFive(3); // 8
?>

Пример: универсальная функция каррирования

<?php
function curry(callable $fn, int $arity = null): callable {
    $reflection = new ReflectionFunction($fn);
    $arity = $arity ?? $reflection->getNumberOfRequiredParameters();

    $curry = function(...$args) use ($fn, $arity, &$curry) {
        if (count($args) >= $arity) {
            return $fn(...$args);
        }
        return function(...$nextArgs) use ($fn, $args, $arity, $curry) {
            return $curry(...array_merge($args, $nextArgs));
        };
    };

    return $curry;
}

$add = function(int $a, int $b, int $c): int {
    return $a + $b + $c;
};

$curriedAdd = curry($add);
$result = $curriedAdd(1)(2)(3); // 6
echo $result;
?>

Каррирование особенно полезно при композиции функций и создании специализированных версий общих функций.

3. Композиция функций и функциональные паттерны

Композиция функций — это создание новой функции путем последовательного применения нескольких функций. Результат одной функции передается на вход следующей.

Пример: композиция "слева направо" (pipe)

<?php
function pipe(callable ...$fns): callable {
    return function($input) use ($fns) {
        $result = $input;
        foreach ($fns as $fn) {
            $result = $fn($result);
        }
        return $result;
    };
}

$trim = fn(string $s): string => trim($s);
$upper = fn(string $s): string => strtoupper($s);
$addExclamation = fn(string $s): string => $s . '!';

$process = pipe($trim, $upper, $addExclamation);
echo $process('  hello world  '); // HELLO WORLD!
?>

Паттерн "Maybe" (функтор)

Функтор — это контейнер, который реализует метод map. В PHP это можно реализовать для обработки значений, которые могут быть null.

<?php
class Maybe {
    private $value;

    private function __construct($value) {
        $this->value = $value;
    }

    public static function just($value): self {
        return new self($value);
    }

    public static function nothing(): self {
        return new self(null);
    }

    public function map(callable $fn): self {
        if ($this->value === null) {
            return self::nothing();
        }
        return self::just($fn($this->value));
    }

    public function getOrElse($default) {
        return $this->value !== null ? $this->value : $default;
    }
}

// Пример использования
$result = Maybe::just('  PHP  ')
    ->map('trim')
    ->map('strtoupper')
    ->getOrElse('default');

echo $result; // PHP

$result2 = Maybe::nothing()
    ->map('trim')
    ->map('strtoupper')
    ->getOrElse('fallback');

echo $result2; // fallback
?>

Паттерн "Either" (монада)

Монада Either представляет собой вычисление, которое может завершиться успешно (Right) или с ошибкой (Left). Это альтернатива исключениям.

<?php
class Either {
    private $isRight;
    private $value;

    private function __construct($value, bool $isRight) {
        $this->value = $value;
        $this->                

Блоги

Рекомендаций по книгам