`make-rename-transformer` не работает внутри другого макроса с использованием кавычек

#macros #racket #rename

#макросы #racket #переименовать

Вопрос:

У меня есть следующий код, который составляет традиционный макрос, eat который использует кавычку ( '(eating food) ), с make-rename-transformer макросом, который должен преобразовываться lunch в sandwich . Полный код:

 #lang racket

(require (for-syntax syntax/parse))

(define-syntax lunch (make-rename-transformer #'sandwich))

(define-syntax (eat stx)
  (syntax-parse stx
    [(_ food)
     #''(eating food)]))

(eat lunch)
  

Поскольку lunch это просто перенос переименования для sandwich , я ожидал бы, что он оценит (eat sandwich) , и таким образом '(eating sandwich) , но когда я запускаю его, я получаю:

 '(eating lunch)
  

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

Ответ №1:

Проблема здесь просто в порядке расширения. В отличие от функций, которые, в любом случае, в первом приближении вычисляются изнутри. Макросы по своей природе расширяются вовне, что является неотъемлемым свойством расширения макросов в языках, подобных Racket.

Итак, проблема в том, что первый шаг макроса expa

 #lang racket
(define-syntax lunch (make-rename-transformer #'sandwich))
'(eating lunch)
  

На этом этапе lunch больше не является идентификатором, а просто базой данных, поэтому переименование transformer здесь не применяется.

Что вам нужно сделать, это указать расширителю макросов развернуть food , прежде чем вы поместите его в кавычку и превратите в datum. Расширитель макросов Racket содержит эту функциональность, [ local-expand ], если вы объедините это с #:when ( syntax/parse версией with-syntax food ), вы можете заставить макрос сначала оценить, что это такое. ,

Собрав все это вместе, ваш код выглядит следующим образом:

 #lang racket

(require (for-syntax syntax/parse))

(define-syntax lunch (make-rename-transformer #'sandwich))

(define-syntax (eat stx)
  (syntax-parse stx
    [(_ food)
     #:with expanded-food (local-expand #'food 'expression #f)
     #''(eating expanded-food)]))

(eat lunch)
  

И когда вы запускаете это, вы получаете:

 '(eating sandwich)