简体   繁体   中英

Why this lisp recursive macro doesn't work?

I have macro let-- (like let* using lambdas) in guile:

(define (let-make-lambdas pairs body)
    (if (null? pairs)
        `((lambda () ,@body))
        `((lambda (,(caar pairs))
            ,(let-make-lambdas (cdr pairs) body))
          ,(cadar pairs))))

(define-macro (let-- pairs . body)
    (let-make-lambdas pairs body))

it works fine when I use an external function to do code generation, but the code below (with is just a macro) doesn't work:

(define-macro (let-- pairs . body)
    (if (null? pairs)
        `((lambda () ,@body))
        `((lambda (,(caar pairs))
            ,(let-- (cdr pairs) body))
          ,(cadar pairs))))

why?

In the second, you don't want

,(let-- (cdr pairs) body)

but rather

(let-- ,(cdr pairs) ,@body)

That is, your direct macro implementation should be

(define-macro (let-- pairs . body)
    (if (null? pairs)
        `((lambda () ,@body))
        `((lambda (,(caar pairs))
            (let-- ,(cdr pairs) ,@body))
          ,(cadar pairs))))

You don't want to evaluate the inner (let-- ...) at macro expansion time; it's part of the source that should be generated. (Of course, it will be macroxpanded very shortly after.) To highlight this, consider a macro that turns

(plus a b c d)

into

(+ a (+ b (+ c d)))

It would need to expand like

(+ ,(car args) (plus ,@(cdr args)))

but not

(+ ,(car args) ,(plus (cdr args)))

because the latter will try to evaluate (plus '(bcd)) , which won't work.

I think Joshua nailed the answer to you problem. I just want to point out that Scheme standard use syntax-rules and syntax-case . It could be something like like this with syntax-rules :

;; make let* with lambdas
(define-syntax let--
  (syntax-rules ()
    ;; base case, last pair
    ((let-- ((key1 value1)) . body) 
     ((lambda (key1) . body ) value1))

    ;; default case, several
    ((let-- ((key1 value1) . kv-pairs) . body) 
     ((lambda (key1) (let-- kv-pairs . body)) value1))))

(let-- ((a 'a) (b a) (c b)) (list a b c)) ; ==> (a a a)

Here is a working Common Lisp version:

(defmacro let1-- (pairs . body)
  (if (null pairs)
      `((lambda () ,@body))
      `((lambda (,(caar pairs))
         (let-- ,(cdr pairs) . ,body))
         ,(cadar pairs))))
> (macroexpand '(let1-- ((a 1) (b 2)) (+ b a)))
((LAMBDA (A) (LET-- ((B 2)) (+ B A))) 1) ;
T
> (let1-- ((a 1) (b 2)) (+ b a))
3

The corresponding Scheme version is, I guess,

(define-macro (let-- pairs . body)
    (if (null? pairs)
        `((lambda () ,@body))
        `((lambda (,(caar pairs))
            (let-- ,(cdr pairs) . ,body))
          ,(cadar pairs))))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM