осень 2026
Летучка 1 · 7 минут · Ваше ФИ: ____________________
\Lambda ::= V \ \mid\ (\Lambda\ \Lambda) \ \mid\ (\lambda V.\,\Lambda)
Переменная x
Аппликация (M\ N)
Абстракция (\lambda x.\,M)
Терм записан со всеми скобками: (((\lambda x.\,(\lambda y.\,(x\ y)))\ a)\ b). Нарисуйте его дерево и запишите терм с минимумом скобок. Отметьте на дереве, какая \lambda связывает каждую переменную. Есть ли здесь свободные переменные?
Минимальная запись (\lambda x\,y.\,x\ y)\ a\ b; x связана внешней \lambda, y — внутренней, а a и b свободны.
Каждая пара скобок в записи — один узел дерева. Внешние скобки — аппликация: слева функция, справа аргумент.
(((λx. (λy. (x y))) a) b)
Снова аппликация: функция (\lambda x.\,(\lambda y.\,(x\ y))) и её аргумент a.
(((λx. (λy. (x y))) a) b)
Скобки вокруг \lambda x — узел абстракции. У него один ребёнок: тело (\lambda y.\,(x\ y)).
(((λx. (λy. (x y))) a) b)
Ещё одна абстракция: \lambda y и её тело (x\ y).
(((λx. (λy. (x y))) a) b)
Последняя пара скобок — аппликация переменной x к переменной y. Дерево готово.
(((λx. (λy. (x y))) a) b)
(λx y. x y) a b
Программа — это терм. Где в нём «вызов функции», который можно выполнить?
(λx y. x y) a b
(λx y. x y) a b
Аргумент a занял место x, узлы @ и \lambda x исчезли.
(λy. a y) b
Второй шаг — так же. Редексов не осталось, это нормальная форма.
a b
Запишите редукцию (\lambda x\,y.\,x\ y)\ a\ b цепочкой преобразований.
Тот же терм, только аргумент теперь y. Сделайте β-шаг в уме — что получилось?
(λx y. x y) y b
Пересадили «в лоб» — получили (\lambda y.\,y\ y)\ b. Но стрелка от нового y перекинулась на \lambda y: переменную захватили.
(λy. y y) b
Откатываемся. Сначала переименуем связанную переменную — терм от этого не меняется, он α-эквивалентен.
(λx y′. x y′) y b
Теперь оба шага проходят спокойно, и свободная y остаётся свободной.
y b
\begin{alignedat}{3} &[x \mapsto N]\ x &&\equiv N \\ &[x \mapsto N]\ y &&\equiv y \\ &[x \mapsto N]\ (P\ Q) &&\equiv ([x \mapsto N]\ P)\ ([x \mapsto N]\ Q) \\ &[x \mapsto N]\ \lambda x.\,P &&\equiv \lambda x.\,P \\ &[x \mapsto N]\ \lambda y.\,P &&\equiv \lambda y.\,[x \mapsto N]\ P &&\quad y \notin FV(N) \\ &[x \mapsto N]\ \lambda y.\,P &&\equiv \lambda y'.\,[x \mapsto N]\ ([y \mapsto y']\ P) &&\quad y \in FV(N) \end{alignedat}
Выполните подстановки:
(а) [y \mapsto x]\ x\ (\lambda y.\,y\ z) \quad (б) [z \mapsto x]\ x\ (\lambda y.\,y\ z) \quad (в) [z \mapsto x\ y]\ x\ (\lambda y.\,y\ z)
(а) x\ (\lambda y.\,y\ z) — под \lambda y менять нечего \quad (б) x\ (\lambda y.\,y\ x) — проход под связыватель \quad (в) x\ (\lambda y'.\,y'\ (x\ y)) — правило 6
Эквивалентны ли термы, и если да, то в каком смысле — синтаксически, α, β или η?
(а) x и x \quad (б) x и y \quad (в) \lambda x.\,x и \lambda y.\,y \quad (г) \lambda x.\,x и (\lambda y.\,y)\ (\lambda x.\,x) \quad (д) \lambda x\,y.\,x\ y и \lambda x.\,x
(а) это один и тот же терм, равенство синтаксическое \quad (б) не эквивалентны \quad (в) α: отличаются только именем связанной переменной \quad (г) β: правый терм редуцируется в левый \quad (д) η: \lambda y.\,x\ y и x ведут себя одинаково на любом аргументе
Одна сущность часто описывается несколькими свойствами: котик — это имя и мурлычность.
Чего мы хотим от такой структуры данных:
barsik = Cat("barsik", 4)barsik.murlycitybarsik.murlycity += 3) не используем: вместо изменения — новое значение Cat(barsik.name, barsik.murlycity + 3)API пары — терм-конструктор \mathbf{pair}, элиминаторы \mathbf{fst} и \mathbf{snd}
Законы пар
Запишите \mathbf{swap}, меняющий компоненты пары местами, через \mathbf{pair}, \mathbf{fst} и \mathbf{snd}. Проверьте по законам: \mathbf{fst}\ (\mathbf{swap}\ (\mathbf{pair}\ N\ M)) =_\beta M.
\begin{aligned} &\mathbf{swap} := \lambda p.\,\mathbf{pair}\ (\mathbf{snd}\ p)\ (\mathbf{fst}\ p) \\ &\mathbf{fst}\ (\mathbf{swap}\ (\mathbf{pair}\ N\ M)) =_\beta \mathbf{fst}\ (\mathbf{pair}\ (\mathbf{snd}\ (\mathbf{pair}\ N\ M))\ \ldots) =_\beta \mathbf{snd}\ (\mathbf{pair}\ N\ M) =_\beta M \end{aligned}
Конструктор принимает два значения и функцию, которой отдаёт оба:
Элиминаторы:
\mathbf{pair} := \lambda x\,y.\,\lambda f.\,f\ x\ y \qquad \mathbf{fst} := \lambda p.\,p\ \mathbf{K} \qquad \mathbf{snd} := \lambda p.\,p\ \mathbf{K}_*
Проверьте, что второй закон пар выполняется.
\begin{alignedat}{2} & &&\ \mathbf{snd}\ (\mathbf{pair}\ N\ M) \\ &\rightsquigarrow_{\mathbf{pair}} &&\ \mathbf{snd}\ ((\lambda x\,y.\,\lambda f.\,f\ x\ y)\ N\ M) \\ &\rightsquigarrow_\beta &&\ \mathbf{snd}\ (\lambda f.\,f\ N\ M) \\ &\rightsquigarrow_{\mathbf{snd}} &&\ (\lambda p.\,p\ \mathbf{K}_*)\ (\lambda f.\,f\ N\ M) \\ &\rightsquigarrow_\beta &&\ (\lambda f.\,f\ N\ M)\ \mathbf{K}_* \\ &\rightsquigarrow_\beta &&\ \mathbf{K}_*\ N\ M \twoheadrightarrow M \end{alignedat}
Итого \mathbf{snd}\ (\mathbf{pair}\ N\ M) \twoheadrightarrow M — дальше пользуемся этим как правилом «большого шага».
API логических величин — конструкторы-константы \mathbf{true} и \mathbf{false}, элиминатор \mathbf{if}
Сформулируйте законы логических величин по аналогии с законами пар.
Конструкторы принимают две ветки и выбирают из них свою:
Элиминатор — выбор уже сделан значением, осталось применить:
Закодируйте \mathbf{not} через \mathbf{if} по таблице истинности:
| b | \neg b |
|---|---|
| true | false |
| false | true |
\mathbf{not} := \lambda b.\,\mathbf{if}\ b\ \mathbf{false}\ \mathbf{true}
А \mathbf{and}? Подсказка: если a истинно, ответ зависит только от b.
\mathbf{and} := \lambda a\,b.\,\mathbf{if}\ a\ b\ \mathbf{false}
API натуральных чисел
Законы для натуральных чисел
Число \mathbf{3} — это \mathbf{suc}\ (\mathbf{suc}\ (\mathbf{suc}\ \mathbf{0})). Посчитаем \mathbf{natElim}\ \mathbf{3}\ F\ Z одними законами, не зная, как числа устроены.
natElim 3 F Z
цепочка
\mathbf{natElim}\ \mathbf{3}\ F\ Z
\mathbf{3} — это \mathbf{suc}\ \mathbf{2}: второй закон снимает один \mathbf{suc} и применяет F один раз.
F (natElim 2 F Z)
цепочка
\mathbf{natElim}\ \mathbf{3}\ F\ Z
\rightsquigarrow_{\mathbf{3}}\ \mathbf{natElim}\ (\mathbf{suc}\ \mathbf{2})\ F\ Z
=_\beta\ F\ (\mathbf{natElim}\ \mathbf{2}\ F\ Z) — закон 2
Ещё раз: \mathbf{2} — это \mathbf{suc}\ \mathbf{1}.
F (F (natElim 1 F Z))
цепочка
\mathbf{natElim}\ \mathbf{3}\ F\ Z
\rightsquigarrow_{\mathbf{3}}\ \mathbf{natElim}\ (\mathbf{suc}\ \mathbf{2})\ F\ Z
=_\beta\ F\ (\mathbf{natElim}\ \mathbf{2}\ F\ Z) — закон 2
\rightsquigarrow_{\mathbf{2}}\ F\ (\mathbf{natElim}\ (\mathbf{suc}\ \mathbf{1})\ F\ Z)
=_\beta\ F\ (F\ (\mathbf{natElim}\ \mathbf{1}\ F\ Z)) — закон 2
И ещё: \mathbf{1} — это \mathbf{suc}\ \mathbf{0}.
F (F (F (natElim 0 F Z)))
цепочка
\mathbf{natElim}\ \mathbf{3}\ F\ Z
\rightsquigarrow_{\mathbf{3}}\ \mathbf{natElim}\ (\mathbf{suc}\ \mathbf{2})\ F\ Z
=_\beta\ F\ (\mathbf{natElim}\ \mathbf{2}\ F\ Z) — закон 2
\rightsquigarrow_{\mathbf{2}}\ F\ (\mathbf{natElim}\ (\mathbf{suc}\ \mathbf{1})\ F\ Z)
=_\beta\ F\ (F\ (\mathbf{natElim}\ \mathbf{1}\ F\ Z)) — закон 2
\rightsquigarrow_{\mathbf{1}}\ F\ (F\ (\mathbf{natElim}\ (\mathbf{suc}\ \mathbf{0})\ F\ Z))
=_\beta\ F\ (F\ (F\ (\mathbf{natElim}\ \mathbf{0}\ F\ Z))) — закон 2
Дошли до \mathbf{0} — первый закон возвращает Z. F применили столько раз, сколько \mathbf{suc} в числе.
F (F (FZ))
цепочка
\mathbf{natElim}\ \mathbf{3}\ F\ Z
\rightsquigarrow_{\mathbf{3}}\ \mathbf{natElim}\ (\mathbf{suc}\ \mathbf{2})\ F\ Z
=_\beta\ F\ (\mathbf{natElim}\ \mathbf{2}\ F\ Z) — закон 2
\rightsquigarrow_{\mathbf{2}}\ F\ (\mathbf{natElim}\ (\mathbf{suc}\ \mathbf{1})\ F\ Z)
=_\beta\ F\ (F\ (\mathbf{natElim}\ \mathbf{1}\ F\ Z)) — закон 2
\rightsquigarrow_{\mathbf{1}}\ F\ (F\ (\mathbf{natElim}\ (\mathbf{suc}\ \mathbf{0})\ F\ Z))
=_\beta\ F\ (F\ (F\ (\mathbf{natElim}\ \mathbf{0}\ F\ Z))) — закон 2
=_\beta\ F\ (F\ (F\ Z)) — закон 1
Закодируйте \mathbf{isEven} одними законами. Рецепт:
Состояние — пара «ответ сейчас, ответ на следующем шаге»: \mathbf{start} := \mathbf{pair}\ \mathbf{true}\ \mathbf{false}, шаг — \mathbf{swap} из разбора пар, \mathbf{isEven} := \lambda n.\,\mathbf{fst}\ (\mathbf{natElim}\ n\ \mathbf{swap}\ \mathbf{start})
Состояние — одно логическое значение: \mathbf{isEven} := \lambda n.\,\mathbf{natElim}\ n\ \mathbf{not}\ \mathbf{true}
Число знает, сколько раз применить шаг s к начальному значению z:
\mathbf{0} := \lambda s\,z.\,z \qquad \mathbf{1} := \lambda s\,z.\,s\ z \qquad \mathbf{2} := \lambda s\,z.\,s\ (s\ z) \qquad \ldots
Элиминатор — снова применить значение к аргументам: \mathbf{natElim} := \lambda n\,s\,z.\,n\ s\ z, то есть \mathbf{I}.
Числа по Пеано — те же самые: \mathbf{suc}\ (\mathbf{suc}\ (\mathbf{suc}\ \mathbf{0})) \twoheadrightarrow \lambda s\,z.\,s\ (s\ (s\ z)), и законы выполняются.
Число — конвейер; \mathbf{natElim} ставит в него трансформации и вход и запускает.
Число — конвейер; \mathbf{natElim} ставит в него трансформации и вход и запускает.
Число — конвейер; \mathbf{natElim} ставит в него трансформации и вход и запускает.
Число — конвейер; \mathbf{natElim} ставит в него трансформации и вход и запускает.
Число — конвейер; \mathbf{natElim} ставит в него трансформации и вход и запускает.
Трансформаций не осталось: \mathbf{natElim}\ \mathbf{3}\ \mathbf{not}\ \mathbf{true} \twoheadrightarrow \mathbf{false}. Какая семантика у терма \mathbf{natElim}\ n\ \mathbf{not}\ \mathbf{true}?
Чётность: это \mathbf{isEven} с прошлого задания, \lambda n.\,\mathbf{natElim}\ n\ \mathbf{not}\ \mathbf{true}
Запишите \mathbf{suc}: результат должен применять s на один раз больше, чем n. Подсказка: воспользуйтесь числом n через \mathbf{natElim}.
Запишите \mathbf{suc}: результат должен применять s на один раз больше, чем n. Подсказка: воспользуйтесь числом n через \mathbf{natElim}.
Новое s — снаружи: \mathbf{suc} := \lambda n.\,\lambda s\,z.\,s\ (\mathbf{natElim}\ n\ s\ z).
Можно и изнутри: \lambda n.\,\lambda s\,z.\,\mathbf{natElim}\ n\ s\ (s\ z) — цепочка та же.
Закодируйте сложение.
Закодируйте сложение.
\mathbf{plus} := \lambda n\,m.\,\lambda s\,z.\,\mathbf{natElim}\ n\ s\ (\mathbf{natElim}\ m\ s\ z).
Или n раз увеличить m на единицу: \mathbf{plus} := \lambda n\,m.\,\mathbf{natElim}\ n\ \mathbf{suc}\ m
| с лекции | \mathbf{I}\quad \mathbf{K}\quad \mathbf{K}_*\quad \mathbf{S}\quad \mathbf{B} |
| пары | \mathbf{pair}\quad \mathbf{fst}\quad \mathbf{snd} |
| логика | \mathbf{true}\quad \mathbf{false}\quad \mathbf{if}\quad \mathbf{not} |
| числа | \mathbf{0}\quad \mathbf{suc}\quad \mathbf{plus} |
Закодируйте аналогичным образом структуры данных в вашем любимом языке программирования.
Эквивалентен ли код?
Нет. \mathbf{K}_*\ q \rightsquigarrow \lambda y.\,y — получилась функция, а k_star(q) — ошибка «missing 1 required positional argument». Правильное прочтение: \lambda x\,y.\,y \equiv \lambda x.\,\lambda y.\,y, то есть функция, которая игнорирует аргумент и возвращает другую функцию:
Закодируем \mathbf{K} := \lambda x.\,\lambda y.\,x:
\mathbf{B} := \lambda f.\,\lambda g.\,\lambda x.\,f\ (g\ x) принимает функции:
compose(f, g) берёт сразу дваЗакодировав пару, мы получили абстракцию по данным:
Прочитать: Sandy Maguire, Algebra-Driven Design; посмотреть: John Hughes — How to specify it! (Lambda Days 2020).
(X) -> R получается (X, (R) -> a) -> astd::variant<Msg1, Msg2> сразу вызывает Impl::processMsg1(...) или Impl::processMsg2(...)Это известно как tagless final, кодирование Чёрча — Бёма — Берардуччи и CPS-преобразование; всё это ещё встретится в курсе.
На самом деле, приоритезировать элиминаторы - более характерно для ОО стиля.
interface Pair<A, B> {
fun <R> fold(f: (A, B) -> R): R
}
fun <A, B> pair(x: A, y: B): Pair<A, B> = object : Pair<A, B> {
override fun <R> fold(f: (A, B) -> R): R = f(x, y)
}
fun <A, B> fst(p: Pair<A, B>): A = p.fold { x, _ -> x }
fun <A, B> snd(p: Pair<A, B>): B = p.fold { _, y -> y }object : Interface — это конструкция для определения анонимного класса-наследника. Аналогичным образом можно сделать через именованные классы.
Практика 1. Программирование в λ-исчислении