jcubic
jcubic

Reputation: 66650

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?

Upvotes: 1

Views: 167

Answers (3)

Sylwester
Sylwester

Reputation: 48775

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)

Upvotes: 2

Joshua Taylor
Joshua Taylor

Reputation: 85913

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 '(b c d)), which won't work.

Upvotes: 3

sds
sds

Reputation: 60074

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

Upvotes: 1

Related Questions