Статья разбирает старую головоломку: написать функцию факториала fact(n) в JavaScript без циклов (for, while), рекурсии и любых объявлений (let, const, function). Звучит нереально, но автор последовательно показывает, как это сделать с помощью лямбда-исчисления на практике.
Сначала идёт классический рекурсивный вариант, который сразу отбрасывается — он вызывает сам себя, а это запрещено. Следующая попытка — взаимная рекурсия: две функции factf и factg вызывают друг друга. Формально каждая не вызывает себя напрямую, но это всё равно рекурсия, её тоже запрещают.
Тогда автор вводит генератор factgen, который принимает себя же в качестве аргумента — паттерн self(self, n). Вызов factgen(factgen, n) разворачивается в n * factgen(factgen, n-1), что в точности повторяет логику факториала. Рекурсии нет, но есть объявление function, которое тоже под запретом.
Автор переходит к анонимным функциям и выделяет ключевой паттерн самоприменения: (x => x(x))(x => x(x)) — это rep(rep), которое в теории редуцируется само в себя. Чтобы сделать его полезным, внутрь вставляют функцию h: x => h(x(x)). Тогда rep(rep) превращается в h(rep(rep)) — почти рекурсия.
Этот механизм упаковывается в комбинатор Y: let Y = (h) => { let rep = x => h(x(x)); return rep(rep) }. Y(f) даёт неподвижную точку f, то есть p = f(p). Если взять factgen (версию, которая принимает одну функцию и возвращает функцию от n), то fact = Y(factgen) решает задачу. Проблема в том, что JavaScript вычисляет аргументы жадно (eager evaluation), и Y(factgen) уходит в бесконечный цикл до того, как начнёт работать.
Решение — обернуть самовызов в дополнительную функцию. Получается комбинатор Z: let Z = (h) => { let rep = x => h(v => x(x)(v)); return rep(rep) }. Отличие от Y — в h передаётся не x(x), а v => x(x)(v). Это откладывает вычисление до момента, когда поступит аргумент. Z(factgen) уже работает: цепочка вызовов раскручивается ровно до базового случая n === 0.
Итог — однострочник без единого объявления, цикла или рекурсии: (f => (x => f(v => x(x)(v)))(x => f(v => x(x)(v))))(cb => n => (n === 0) ? 1 : n * cb(n - 1)). В теории это Y-комбинатор (универсальный генератор рекурсии), а рабочая версия для JavaScript называется Z-комбинатором.