Как вызвать другие макросы из макроса 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 выражение.

Другие вопросы по тегам