Раздел 21 · Haskell

Модель вычислений: thunk, WHNF и ленивость

middle-senior~45 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Модель вычислений: thunk, WHNF и ленивость

Ленивость это не флажок оптимизации, а модель вычислений. До сих пор мы пользовались её следствиями на веру: бесконечные списки из урока 01, ранний выход и ленивый foldl из урока 05. Сегодня заглядываем под капот. Граф редукции, thunk, WHNF против NF, sharing, seq и $!. Отсюда выводится всё остальное: почему именно foldl копит память, как работает строгий аккумулятор и что такое space leak.

Идея

В уроке 01 ты разобрал стратегию: Haskell редуцирует в нормальном порядке, то есть сначала подставляет аргумент, не вычисляя его, и считает только когда результат действительно понадобится. Там же был обещан тёмный угол. Пришли за ним.

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

Пока узел не понадобился, он лежит неподсчитанным. У этого отложенного узла есть имя.

Это thunk: коробка с рецептом вычисления, а не с готовым блюдом. Любой аргумент функции, любой элемент списка, любой let, всё это по умолчанию рождается thunk-ом. Считать начинаем, только когда снаружи кто-то требует значение. Это требование называют форсированием (forcing).

Возникает вопрос: до какой степени форсировать? Не всегда нужно считать до конца. И тут прячется самое полезное различие урока.

ghci> let pair = (1 + 2, 3 + 4)

Чтобы узнать, что pair это пара (а не, скажем, undefined), достаточно довести его до внешнего конструктора (,). Содержимое 1 + 2 и 3 + 4 при этом считать не обязательно: оно остаётся двумя thunk-ами внутри пары. Такая частично вычисленная форма, до первого конструктора снаружи, называется WHNF, слабая головная нормальная форма.

А NF, полная нормальная форма, это когда thunk-ов не осталось вовсе, выражение вычислено до самого дна.

Разница между WHNF и NF это весь сегодняшний урок в одном кадре. Запусти первый пресет в виджете и пройди по шагам: видно, как форсирование сначала добирается до пары (WHNF), и только потом, если кто-то просит, лезет внутрь компонентов (NF).

Держи эту модель в голове: граф из thunk-ов, форсирование по запросу, две глубины готовности. Дальше только применяем её к реальному коду.

премиум-урок

Продолжение в премиуме: 1 200 ₽ в месяц или 10 000 ₽ в год. Вместе с ним серверные проверки задач и школьные токены AI-коуча.

Оплата пока не подключена: премиум и курсы открываются промокодом.