Как вызвать другие макросы из макроса Chicken Scheme?
Я пытаюсь перейти от Common Lisp к Chicken Scheme, и у меня много проблем.
Моя текущая проблема заключается в следующем: Как я могу написать макрос (предположительно с использованием define-syntax
?) что вызывает другие макросы?
Например, в Common Lisp я мог бы сделать что-то вроде этого:
(defmacro append-to (var value)
`(setf ,var (append ,var ,value)))
(defmacro something-else ()
(let ((values (list))
(append-to values '(1)))))
Принимая во внимание, что в Схеме эквивалентный код не работает:
(define-syntax append-to
(syntax-rules ()
((_ var value)
(set! var (append var value)))))
(define-syntax something-else
(syntax-rules ()
((_)
(let ((values (list)))
(append-to values '(1))))))
append-to
макрос не может быть вызван из something-else
макро. Я получаю сообщение об ошибке append-to
"переменная" не определена.
Согласно всей информации, которую мне удалось собрать из Google и других источников, макросы оцениваются в закрытой среде без доступа к другому коду. По сути, ничего не существует - кроме встроенных функций Scheme и макросов - когда выполняется оценка макроса. Я пытался использовать er-macro-transformer
, syntax-case
(что в любом случае сейчас не рекомендуется в Chicken) и даже procedural-macros
модуль.
Конечно, все назначение макросов состоит в том, что они построены на других макросах, чтобы избежать повторения кода. Если макросы должны быть написаны изолированно, они, на мой взгляд, бесполезны.
Я исследовал другие реализации Схемы, и мне больше не повезло. Кажется, это просто невозможно сделать.
Может кто-нибудь помочь мне с этим, пожалуйста?
1 ответ
Похоже, вы путаете время расширения с временем выполнения. syntax-rules
Пример, который вы привели, расширится до набора let +, что означает, что добавление произойдет во время выполнения.
syntax-rules
просто переписывает ввод для данного вывода, расширяя макросы, пока больше нечего расширять. Если вы действительно хотите выполнить какое-то вычисление во время раскрытия, единственный способ сделать это с процедурным макросом (это также происходит в вашем defmacro
Пример кл).
В Схеме уровни оценки строго разделены (это делает возможным отдельную компиляцию), поэтому процедура может использовать макросы, но сами макросы не могут использовать процедуры (или макросы), определенные в одном и том же фрагменте кода. Вы можете загружать процедуры и макросы из модуля для использования в процедурных макросах, используя синтаксис использования. Существует ограниченная поддержка определения вещей, запускаемых во время расширения синтаксиса, заключая их в начало синтаксиса.
Посмотрите, например, этот вопрос SO или это обсуждение в списке рассылки ikarus-users. Составляемые и компилируемые макросы Мэтью Флатта объясняют теорию, лежащую в основе этого, более подробно.
Мышление "фазового разделения" является относительно новым в мире Scheme (обратите внимание, что статья Флатта относится к 2002 году), поэтому вы найдете довольно много людей в сообществе Scheme, которые все еще немного смущены этим. Причина, по которой он "новый" (хотя у Scheme были макросы в течение долгого времени), заключается в том, что процедурные макросы стали частью стандарта только с R6RS (и возвращены в R7RS, потому что syntax-case
является довольно спорным), поэтому необходимость их жесткого определения до сих пор не была проблемой. Для более "традиционных" реализаций Lispy в Scheme, где время компиляции и время выполнения объединяются, это никогда не было проблемой; Вы можете просто запустить код всякий раз, когда.
Если вернуться к вашему примеру, он будет работать нормально, если вы правильно разделите фазы:
(begin-for-syntax
(define-syntax append-to
(ir-macro-transformer
(lambda (e i c)
(let ((var (cadr e))
(val (caddr e)))
`(set! ,var (append ,var ,val)))))) )
(define-syntax something-else
(ir-macro-transformer
(lambda (e i c)
(let ((vals (list 'print)))
(append-to vals '(1))
vals))))
(something-else) ; Expands to (print 1)
Если вы положите определение append-to
в отдельном модуле, и вы use-for-syntax
это, это должно работать также. Это также позволит вам использовать один и тот же модуль как в макросах, которые вы определяете в теле кода, так и в процедурах, просто требуя его обоих в use
и use-for-syntax
выражение.