Введение

Оператор управления потоком в функциональном программировании

В языке программирования Scheme в качестве оператора управления потоком используется вызов процедуры с текущим продолжением, сокращенно call/cc. Он был принят несколькими другими языками программирования. Принимая функцию f в качестве единственного аргумента, (call/cc f) в выражении применяется к текущему продолжению этого выражения. Например, ((call/cc f) e2) эквивалентно применению f к текущему продолжению выражения. Текущее продолжение получается заменой (call/cc f) на переменную c, связанную с лямбда-абстракцией, таким образом, текущее продолжение будет (lambda (c) (c e2)). Применение функции f к нему дает конечный результат (f (lambda (c) (c e2))). В качестве дополнительного примера, в выражении (e1 (call/cc f)), продолжение для подвыражения (call/cc f) равно (lambda (c) (e1 c)), поэтому всё выражение эквивалентно (f (lambda (c) (e1 c))). Другими словами, он делает "снимок" текущего контекста управления или состояния программы как объекта и применяет к нему f. Объект продолжения является значением первого класса и представлен в виде функции, а применение функции – его единственная операция. Когда объект продолжения применяется к аргументу, существующее продолжение удаляется и на его место восстанавливается примененное продолжение, так что выполнение программы продолжится с точки, в которой продолжение было захвачено, а аргумент продолжения становится "возвращаемым значением" вызова call/cc. Продолжения, созданные с помощью call/cc, могут быть вызваны несколько раз, и даже вне динамической области применения call/cc. В информатике, представление неявного состояния программы в виде объекта называется реификацией. (Scheme синтаксически не различает применение продолжений и функций.) С помощью call/cc можно реализовать различные сложные операторы управления из других языков всего за несколько строк кода, например, оператор amb Маккарти для недетерминированного выбора, отслеживание в стиле Prolog, корутины в стиле Simula 67 и их обобщения, генераторы в стиле Icon, или движки и потоки, или даже малоизвестный COMEFROM.

Критика

Олег Киселев, автор реализации ограниченных продолжений для OCaml и разработчик интерфейса прикладного программирования (API) для манипулирования ограниченным стеком с целью реализации операторов управления, выступает за использование ограниченных продолжений вместо полных стековых продолжений, с которыми работает call/cc: "Использование call/cc в качестве базовой управляющей конструкции, на основе которой должны реализовываться все остальные средства управления, оказывается неудачным решением. Производительность, утечки памяти и ресурсов, простота реализации, удобство использования и понятность рассуждений – все говорит против call/cc."

Отношение к неконструктивной логике

Корреспонденция Карри-Ховарда между доказательствами и программами связывает call/cc с законом Пирса, который расширяет интуиционистскую логику до неконструктивной, классической логики: ((α → β) → α) → α. Здесь ((α → β) → α) является типом функции f, которая может либо непосредственно возвращать значение типа α, либо применить аргумент к продолжению типа (α → β). Поскольку существующий контекст удаляется при применении продолжения, тип β никогда не используется и может считаться ⊥, пустым типом. Принцип устранения двойного отрицания ((α → ⊥) → ⊥) → α сопоставим с вариантом call/cc, который ожидает, что его аргумент f всегда будет вычислять текущее продолжение, не возвращая значение обычным образом. Встраивания классической логики в интуиционистскую логику связаны с преобразованием в стиле передачи продолжения.