Ваш макрос alambda
негигиеничен, а негигиеничные макросы плохо сочиняются. Особенно плохо компонуются негигиеничные макросы, создающие идентификаторы на основе уже используемых для другой цели подтерминов. Одним из решений является создание вспомогательного макроса, который принимает «лексический контекст» для новых идентификаторов в качестве отдельного аргумента. Затем создайте производные макросы из этого.
(define-syntax alambda/lctx
(lambda (stx)
(syntax-case stx ()
[(alambda lctx formals . body)
(with-syntax ([name (datum->syntax #'lctx 'self)])
#'(letrec ([name (lambda formals . body)])
name))])))
(define-syntax alambda
(lambda (stx)
(syntax-case stx ()
[(alambda formals . body)
#'(alambda/lctx alambda formals . body)])))
(define-syntax apply-alambda
(lambda (stx)
(syntax-case stx ()
[(apply-alambda formals argv . body)
#'((alambda/lctx apply-alambda formals . body) . argv)])))
(apply-alambda [x] [5] (if (= 0 x) 1 (* x (self (- x 1)))))
В макросе alambda
лексический контекст, используемый для создания связующего self
, берется из ссылки на сам макрос. Макрос делает этот аргумент явным в вызове alambda/lctx
. Аналогично с apply-alambda
--- и если вы хотите создать еще один макрос, который расширяется до apply-alambda
, то вам также следует создать помощник apply-alambda/lctx
.
(В Racket лексический контекст прикрепляется не только к идентификаторам, но и к структуре списка («круглые скобки»), и негигиеничные макросы обычно используют весь синтаксический объект, как в (datum->syntax stx 'self)
. Это позволяет избежать необходимости в отдельном вспомогательный макрос.)
Примечание: использование define-syntax-rule
для определения alambda
и apply-alambda
не работает, потому что оно фактически не привязывает идентификатор к позиции оператора.
У вас может возникнуть соблазн сделать apply-alambda
вызовом alambda
с идентификатором alambda
с лексическим контекстом, соответствующим использованию формы apply-alambda
, например:
(define-syntax bad-apply-alambda
(lambda (stx)
(syntax-case stx ()
[(apply-alambda formals argv . body)
(with-syntax ([alambda (datum->syntax #'apply-alambda 'alambda)])
#'((alambda formals . body) . argv))])))
Эта версия неверна. Он ведет себя неправильно, если alambda
не привязан (или привязан не к тому объекту) в области, где используется bad-apply-alambda
. Например:
(let ([alambda 5])
(bad-apply-alambda [x] [5] (if (= 0 x) 1 (* x (self (- x 1))))))
person
Ryan Culpepper
schedule
17.12.2019