; Having this definition that creates identifier `self'
(define-syntax alambda
(lambda (stx)
(syntax-case stx ()
[(alambda lambda-list . body)
(with-syntax ([name (datum->syntax #'alambda 'self)])
#'(letrec ([name (lambda lambda-list . body)])
name))])))
; I want to "compose" it with another macro
(define-syntax-rule [apply-alambda args argv . body]
((alambda args . body) . argv))
; But then it doesn't work (while alambda itself does)
(apply-alambda [x] [5] (if (= 0 x) 1 (* x (self (- x 1)))))
; => Unbound variable: self
; (expected 120)
How to prevent apply-alambda
from renaming self
?
I tried to use define-macro
which also didn't work but for a different reason:
(defmacro apply-alambda [args argv . body]
((alambda args . body) . argv))
; => lambda: bad lambda in form (lambda args . body)
Here, I don't even know what went wrong
Your alambda
macro is unhygienic, and unhygienic macros don't compose well. Unhygienic macros that create identifiers based on subterms already used for another purpose compose especially poorly. One solution is to create a helper macro that takes the "lexical context" for the new identifiers as a separate argument. Then create the derived macros from that.
(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)))))
In the alambda
macro, the lexical context used to create the self
binder is taken from the reference to the macro itself. The macro makes that argument explicit in the call to alambda/lctx
. Likewise with apply-alambda
---and if you want to create another macro that expands into apply-alambda
, then you should likewise create an apply-alambda/lctx
helper.
(In Racket, lexical context is attached not only to identifiers but also to the list structure (the "parentheses"), and it is common for unhygienic macros to use the whole syntax object, as in (datum->syntax stx 'self)
. This avoids the need for a separate helper macro.)
Note: using define-syntax-rule
to define alambda
and apply-alambda
doesn't work, because it doesn't actually bind the identifier in operator position.
You might be tempted to make apply-alambda
call alambda
with an alambda
identifier with the lexical context corresponding to the use of the apply-alambda
form, like this:
(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))])))
This version is wrong. It behaves incorrectly if alambda
is not bound (or is bound to the wrong thing) in the scope where bad-apply-alambda
is used. For example:
(let ([alambda 5])
(bad-apply-alambda [x] [5] (if (= 0 x) 1 (* x (self (- x 1))))))