фона
Ядра Clojure язык не поддерживает первый класс продолжений. Это и тот факт, что JVM не обеспечивает способ захвата текущего продолжения, означает, что нет способа реализовать letcc
, что является удовлетворительным для всех ситуаций.
Однако в некоторых ситуациях можно реализовать продолжения. В частности, если у вас есть весь код (то есть код, в котором вы должны записывать продолжения), вы можете использовать стиль продолжения-прохождения (CPS). В принципе, вы добавляете дополнительный параметр для каждой функции. Этот параметр является функцией, которая представляет собой продолжение этого вызова. Вы «возвращаете» значение, вызывая функцию продолжения. Конечно, этот стиль - это боль, чтобы писать самостоятельно - но, к счастью, это преобразование, которое мы можем легко применить к конкретному коду с помощью макросов.
Сам по себе CPS непригоден для платформ, которые не выполняют оптимизацию хвостовых вызовов (TCO). Поскольку последний шаг любой функции в CPS заключается в вызове другой функции, без TCO стек быстро переполняется, за исключением самых тривиальных вычислений. Эта проблема может быть решена путем использования thunking и trampolining.
Solutions
Как я упоминал выше, вы можете написать превратить свой собственный КПС с помощью макросов. Тем не менее, я бы пригласил вас проверить мою библиотеку pulley.cps, которая уже делает это за вас. Есть альтернативы, но, насколько я знаю, pulley.cps единственная библиотека Clojure, которая обеспечивает все следующие:
call-cc
/let-cc
- Бесшовных звонков между «родным» (нетрансформированным) и трансформируют код
- Exception (
try
/catch
/finally
) поддержка
binding
формы (они правильно хвостовой рекурсией тоже!)
- Позволяет обеспечить CPS Versio п существующей собственной функции (это необходимо, если вы хотите, чтобы захватить продолжение в рамках этой функции)
Альтернатив включают в себя:
- delimc предоставляет библиотеку для делимитированных продолжений. Это не выглядит очень полным (например,
binding
не работает, поскольку он не понимает блок try
/finally
) и не был затронут через 4 года.
- algo.monads является монадной библиотекой для Clojure. Существует сильная и интересная связь между монадами и продолжениями, а algo.monads - продолжение монады.Хотя монадический стиль не совсем такой же понятен, он имеет то преимущество, что делает эффект более явным, что может помочь в инкапсуляции кода, который использует эффекты управления из кода, который этого не делает. Плюс,
do
обозначение (например, макрос domonad
) сильно размывает линии между прямым и монадическим стилем.
Этот вопрос немного неясно. Вы ищете лучшего 'letcc', чем ваш, или вам интересно, что с ним не так? Я не думаю, что можно сделать «настоящий» 'letcc' в Clojure, поскольку он не имеет первоклассного продолжения. – molbdnilo