Почему не существует примитивного "вызова с текущими продолжениями" в Common Lisp?

в последнее время я изучаю различия между схемой и общим Lisp относительно подхода, который эти два языка имеют к продолжениям.

Я заметил, что общий подход Lisp более консервативен, чем схема подхода.

кроме того, схема примитивной call-with-current-continuation, часто обозначаемый call/cc, который не имеет эквивалента в спецификации ANSI Common Lisp (хотя есть некоторые библиотеки, которые пытаются реализовать их.)

кто-нибудь знает причину, по которой было принято решение не создавать аналогичный примитив в спецификации ANSI Common Lisp?

спасибо заранее.

4 ответов


Common Lisp имеет подробную модель компиляции файлов как часть стандартного языка. Модель поддерживает компиляцию программы в объектные файлы в одной среде и загрузку их в образ в другой среде. В схеме нет ничего сопоставимого. Нет!--0--> или compile-file, load-time-value или такие понятия, как что такое объект externalizable, как семантика в скомпилированный код должен согласиться с интерпретируемым кодом. Lisp имеет способ иметь встроенные функции или не иметь их, и поэтому в основном вы с большой точностью контролируете, что происходит при повторной загрузке скомпилированного модуля.

напротив, до недавнего пересмотра отчета схемы язык схемы был полностью молчит на тему о том, как программа Схемы разбита на несколько файлов. Для этого не было предусмотрено никаких функций или макросов. Посмотрите на R5RS, под 6.6.4 Интерфейс Системы. Все, что у вас есть очень расплывчато :

необязательный процедура: (загрузить имя файла)

Filename должно быть строкой, называющей существующий файл, содержащий исходный код схемы. Процедура load считывает выражения и определения из файла и оценивает их последовательно. Не указано, печатаются ли результаты выражений. Процедура загрузки не влияет на значения, возвращаемые текущим портом ввода и текущим портом вывода. Load возвращает неопределенное значение.

обоснование: для удобоносимости, загрузка должна работать с исходными файлами. Его работа с другими типами файлов обязательно варьируется между реализациями.

Итак, если это степень вашего видения о том, как приложения строятся из модулей, и все детали за пределами этого остаются для разработчиков, конечно, небо является пределом в отношении изобретения семантики языка программирования. Обратите внимание на часть обоснования: if load определяется как работа с исходными файлами (все остальное является бонусом любезно предоставлено исполнителями), то это не что иное, как текстовый механизм включения, такой как #include на языке C, и поэтому приложение схемы-это действительно только один текстовый текст, который физически распространяется на несколько текстовых файлов, собранных load.

если вы думаете о добавлении какой-либо функции в Common Lisp, вам нужно подумать о том, как она вписывается в его подробную динамическую модель загрузки и компиляции,при сохранении хорошей производительности что пользователи ожидают.

если функция, о которой вы думаете, требует глобальной оптимизации всей программы (при которой система должна видеть структурный исходный код всего), чтобы программы пользователей не работали плохо (и, в частности, программы, которые не используют эту функцию), тогда она не будет летать.

в частности, что касается семантики продолжений, есть проблемы. В обычной семантике области блока, как только мы покидаем область и выполняем очистку, это ушло; мы не можем вернуться к этой области во времени и возобновить вычисления. Обычная шепелявость в этом смысле обычна. У нас есть unwind-protect construct, который выполняет безусловные действия очистки, когда область завершается. Это основа для таких функций, как with-open-file который предоставляет открытый объект дескриптора файла в область блока и гарантирует, что он закрыт независимо от того, как область блока завершается. Если продолжение ускользает из этой области, это продолжение больше не имеет допустимого файла. Мы не можем просто не закройте файл, когда мы покидаем область, потому что нет никакой гарантии, что продолжение когда-либо будет использоваться; то есть, мы должны предположить, что область фактически оставлена навсегда и своевременно очистить ресурс. Пластырь решение для такого рода проблемы dynamic-wind, что позволяет добавлять обработчики при входе и выходе в область блока. Таким образом, мы можем повторно открыть файл, когда блок перезапускается продолжением. И не только снова открыть, но фактически расположите поток в точно таком же положении в файле и так далее. Если поток был на полпути через декодирование некоторого символа UTF-8, мы должны поместить его в то же состояние. Так что если Lisp получил продолжения, либо они будут нарушены различными with- конструкции, которые выполняют очистку (плохая интеграция), иначе эти конструкции должны были бы приобрести гораздо более волосатую семантику.

существуют альтернативы продолжениям. Некоторые виды использования продолжения не имеют существенного значения. По существу, та же организация кода может быть получена с закрытиями или перезапусками. Кроме того, существует мощная конструкция языка/операционной системы, которая может конкурировать с продолжением: а именно, поток. Хотя продолжения имеют аспекты, которые не моделируются хорошо потоками (и не говоря уже о том, что они не вводят в код тупики и условия гонки), они также имеют недостатки по сравнению с потоками: например, отсутствие фактического параллелизма для использования нескольких процессоров или приоритизация. Многие проблемы, выражаемые с продолжениями, могут быть выражены потоками почти так же легко. Например, продолжения давайте писать рекурсивный спуск парсер, который выглядит как поток, как объект, который просто возвращает прогрессивные результаты, как он анализирует. Код на самом деле является рекурсивным анализатором спуска, а не государственной машиной, которая имитирует его. Потоки позволяют нам делать то же самое: мы можем поместить парсер в поток, завернутый в" активный объект", у которого есть " получить следующее" метод, который вытаскивает вещи из очереди. Как Парсеры потока, вместо того, чтобы возвращать продолжение, он просто бросает объекты в очередь (и, возможно, блокирует какой-то другой поток, чтобы удалить их). Продолжение выполнения обеспечивается возобновлением этого потока; его контекст потока является продолжением. Не все модели потоков страдают от условий гонки (как много); есть, например, кооперативная резьба, при которой один поток выполняется за раз, и переключатели потоков потенциально имеют место только тогда, когда поток делает явный вызов в ядро потока. Основные распространенные реализации Lisp имели легкие потоки (обычно называемые "процессами") в течение десятилетий и постепенно перешли к более сложным потокам с поддержкой многопроцессорной обработки. Поддержка потоков уменьшает потребность в продолжениях и является большим приоритетом реализации, поскольку время выполнения языка без поддержки потоков находится в технологическом недостатке: невозможность в полной мере использовать аппаратное обеспечение ресурсы.


Это то, что Кент М. Питман, один из дизайнеров Common Lisp, должен был сказать по этой теме:из comp.ленг.шепелявить!--2-->


конструкция схемы была основана на использовании вызовов функций для замены наиболее распространенных структур управления. Вот почему схема требует исключения хвостового вызова: она позволяет преобразовать цикл в рекурсивный вызов без потенциального исчерпания пространства стека. И основной подход к этому -продолжение прохождения стиле.

Common Lisp является более практичным и менее педагогическим. Он не диктует стратегии реализации, и продолжения не требуются для реализовать его.


Common Lisp является результатом усилий по стандартизации нескольких видов практического (прикладного) Lisps (таким образом, "Common"). CL ориентирован на реальные приложения, поэтому он имеет более "специфические" функции (например,handler-bind) вместо call/cc.

схема была разработана как небольшой чистый язык для обучения CS, поэтому она имеет фундаментальное call/cc который можно использовать для реализации других инструментов.

см. также может вызывать-с-текущим-продолжение быть осуществляться только с лямбдами и замыканиями?