Схема не может найти функцию внутри макроса во время компиляции

У меня есть пример кода:

#!/usr/bin/guile -s
!#

(define (process body)
    (list 'list (map (lambda (lst)
                       (list 'quote (car lst)))
                       body)))

(defmacro macro (body)
  (list 'quote (process body)))

(display (macro ((foo bar) (bar baz))))
(newline)

он запускается, но у меня есть ошибка от компилятора

ERROR: Unbound variable: process

функции внутри макросов должны быть разрешены, почему я получил эту ошибку?


person jcubic    schedule 27.03.2017    source источник


Ответы (1)


Функции внутри макросов разрешены в Guile и в большинстве других диалектов Scheme.

Однако ключевой вопрос заключается в следующем: какие функции доступны макросу для вызова в процессе расширения?


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

  • Почему не будет такой функции? Вот один из примеров: что, если тело функции использует макрос, который вы сейчас определяете? Тогда у вас будет небольшая проблема с курицей и яйцом. Функция должна будет запустить макрос для компиляции (поскольку использование макроса в теле должно быть расширено во время компиляции) ... Но макросу потребуется скомпилированная функция, доступная даже для запуска!

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

Одна из моих любимых статей, описывающих эту проблему и конкретное решение, принятое MzScheme (теперь известное как Racket), — это "You Want It When", статья Мэтью Флэтта.


Таким образом, это проблема, с которой должен каким-то образом справляться любой диалект Scheme с процедурной системой макросов, и Guile не является исключением.

В случае с Guile одно исправление, которое прямо задокументировано в руководстве по Guile, заключается в использовании eval-when специальная форма, позволяющая указать, на каких этапах должно быть доступно конкретное определение.

(Упомянутый выше документ «Вы хотите, когда» описывает некоторые проблемы с eval-when, но, поскольку это то, что описано в руководстве Guile, я пока буду придерживаться его. Я рекомендую после того, как вы поймете eval-when, что вы затем посмотрите на решение Racket и посмотрите, предлагает ли Guile что-то подобное.)


Итак, в вашем случае, поскольку вы хотите, чтобы функция process была доступна во время компиляции (для использования в определении макроса), вы можете написать:

#!/usr/bin/guile -s
!#

(eval-when (expand)
  (define (process body)
    (list 'list (map (lambda (lst)
                       (list 'quote (car lst)))
                     body))))

(defmacro macro (body)
  (list 'quote (process body)))

(display (macro ((foo bar) (bar baz))))
(newline)
person pnkfelix    schedule 27.03.2017