How do you write an MIT Scheme macro to return a lambda form?
I'm baffled by trying to create the equivalent of this trivial (in Common Lisp) macro in MIT Scheme:
(defmacro funcify (exp)
`(lambda (x) ,exp))
This is for a simple personal project, a numerical equation solver based on the functions built in the second SICP lecture. I don't care that this macro is not "safe" or "hygienic" or will capture a variable if the exp references any symbols other than 'x. I'd like to be able to write
(solv '(* 60 x) '(* 90 (- x 1)))
where solv is:
(define (solv lh-exp rh-exp)
(solve (funcify lh-exp) (funcify rh-exp)))
instead of having to type
(solve (lambda (x) (* 60 x)) (lambda (x) (* 90 (- x 1))))
But can't figure out how to do this using MIT Scheme syntax-rules.
I've tried this but it doesn't work:
(define-syntax funcify
(syntax-rules ()
((funcify y) (lambda (x) y))))
;Value: funcify
(funcify x)
;Value 17: #[compound-procedure 17]
((funcify x) 10)
;Unbound variable: x
I've开发者_如何学JAVA tried other things probably not worth mentioning involving eval
but to no avail.
Also, references to good tutorials (not references) on Scheme's macro system that start with small simple examples and build up, with ample commentary, and in particular show how to convert backquote-comma style LISP macros (which to me are highly intuitive) to Scheme's syntax macro system would be great.
It cannot be done in syntax-rules
. End of story.
Injecting an arbitrary identifier (x
, in your case) into the output expression requires breaking hygiene, and syntax-rules
does not provide any means to break hygiene. You will need to use a lower-level macro system to do this. MIT Scheme uses explicit renaming (see Matthias Benkard's answer), but for other Scheme implementations that use syntax-case
, you can do it thus:
(define-syntax funcify
(lambda (stx)
(syntax-case stx ()
((_ body)
(with-syntax ((x (datum->syntax stx 'x)))
#'(lambda (x)
body))))))
The key is the (datum->syntax stx 'x)
bit, which injects the symbol x
as if it were in the syntactic context of the funcify
invocation.
By the way, your solv
must also be a macro, not a procedure, but at least it can be a syntax-rules
macro:
(define-syntax solv
(syntax-rules ()
((_ lhs rhs) (solve (funcify lhs) (funcify rhs)))))
You can do basically the same thing as with defmacro
by using explicit-renaming macros. The only significant difference is that you will have to destructure the input form yourself:
(define-syntax funcify
(er-macro-transformer
(lambda (form rename cmp)
(let ((exp (cadr form)))
`(,(rename 'lambda) (x) ,exp)))))
精彩评论