1

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?

jcubic
  • 61,973
  • 54
  • 229
  • 402
  • Forgot a parenthesis? :P –  Oct 23 '13 at 18:30
  • The macro call does not seem to correspond to its definition: the dot is missing. – sds Oct 23 '13 at 19:28
  • @sds The `.` is used for defining what would be `&rest` lists in Common Lisp. In the first case, jcubic is defining a functional to perform the code expansion, and the _macro_ _does_ include the `.` – Joshua Taylor Oct 23 '13 at 19:38
  • @JoshuaTaylor: what I meant was that the invocation of the macro must either have a `.` (as in my answer) or a `@` (as in yours) – sds Oct 23 '13 at 19:47
  • @sds Oh, I see what you mean. The `(let-- ...)` _within_ the definition could have been `(let-- (cdr pairs) . body)`. Right (as long as the other issue was resolved). Sorry I misunderstood. – Joshua Taylor Oct 23 '13 at 20:13

3 Answers3

3

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.

Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
2

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)
Sylwester
  • 47,942
  • 4
  • 47
  • 79
  • I prefer lisp-macros and scheme implementations that support them, but nevertheless thanks for the answer. – jcubic Oct 24 '13 at 07:12
1

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))))
sds
  • 58,617
  • 29
  • 161
  • 278