#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)