1

There were similar question but not with syntax macros, I though that the difference is that one macro can't see the other like letrec and let with functions.

But this works the same with letrec-syntax and let-syntax

(let-syntax ((foo (lambda (x) `(bar (list ',(car x) ',(caadr x)))))
             (bar (lambda (x) `(display ',x))))
    (foo (list 1 2 3)))

Can you show example of code where those two macros/syntax differ?

EDIT:

Found this:

(let-syntax ((bar (lambda (x) `(display ',x)))
             (foo (lambda (x) (bar x))))
   (foo (list 1 2 3)))

that don't work with let-syntax and it work with letrec-syntax, but what is the benefit of this? If those local variables can't be functions (the output is (bar x)), what is the purpose of let-syntax? I need example where it can be used in the code, where you don't need letrec-syntax and it would be enough to have let-syntax.

jcubic
  • 61,973
  • 54
  • 229
  • 402

1 Answers1

4

It is the same as let and letrec. With let you cannot expect the binding exist when evaluating the values. This only affects procedure/lambda/closure creation since all procedures are called with the environment they are created in.

(define (test v)
  (list 'default v))

(let ((test (lambda (v) 
              (if (zero? v)
                  (list 0)
                  (cons v (test (- v 1)))))))
  (test 5))
; ==> (5 default 4)

(letrec ((test (lambda (v)
                (if (zero? v)
                    (list 0)
                    (cons v (test (- v 1)))))))
  (test 5))
; ==> (5 4 3 2 1 0)

So in the let example the local let is not in the body of the closure, because it does not exist when the values are evaluated. In practice if you expand let to its equivalent lambda form you see why it calls the global test:

((lambda (test) (test 5))
 (lambda (v)
   (if (zero? v)
       (list 0)
       (cons v (test (- v 1))))))

Do you see why it doesn't call itself? letrec is specifically made to enable to create local procedures that has themselves as binding to solve this little problem. For syntax-rules it has the same binding properties for the similar names, but we are dealing with syntax so we need to make the steps not rely on runtime arithmetic:

(define-syntax stest
  (syntax-rules ()
    ((_ v . rest) '(default v))))

(let-syntax ((stest (syntax-rules () 
                      ((_ v . rest) (cons 'v (stest . rest)))
                      ((_) '()))))
  (stest 5 4 3 2 1 0))
; ==> (5 default 4)

(letrec-syntax ((stest (syntax-rules () 
                         ((_ v . rest) (cons 'v (stest . rest)))
                         ((_) '()))))
  (stest 5 4 3 2 1 0))
; ==> (5 4 3 2 1 0)

Again the letrec-syntax makes sure stest is available in the environment of the syntax-rules tranformer so that it matches itself instead of the top level macro.

As for your examples, they are not Scheme. It might be that they will work in some specific scheme implementation as a defacto extra functionality, but they will not work in any R5RS, R6RS, or R7RS implementation like my examples. R6RS has syntax-case as an additional transformer and I'm guessing R7RS-large will have additional transformers as well. If you are after defacto behaviour you need to tag a specific implementation.

Sylwester
  • 47,942
  • 4
  • 47
  • 79
  • My examples work with any implementation that have quasiquote, usually scheme implemention have them as well as lisp macros, I'm trying to figure out how just `letrec-syntax` and `let-syntax` should work, and in Kawa it works with quasiquote since define-syntax and let(rec)-syntax macros can just have lambda that can work like normal macro so it can just use quasiquote (found this in Dybvig article and it works). I'm more familar with lisp macros that's why I use quasiquote. I'm in process of writing scheme macro system in my JavaScript implementation, but I do this one step at the time. – jcubic Apr 11 '20 at 15:17
  • Correction: I think that in Kawa I would need to do one extra step and evaluate the result of lambda call in let-syntax, but in my implementation they are just simple let with macros. Maybe I should have some kind of syntax macro like Kawa have. Is it possible to somehow use quasiquote with let-syntax, so I can implement just let-syntax without the need to implement the rest of the system? – jcubic Apr 11 '20 at 15:21
  • 1
    @jcubic quasiquote is just fancy syntax sugering for a `cons` expression that produces the same structure. simple fecmacro style macros does not have the same environment restrictions as syntax-rules or syntax-case. The expansion should not borrow introduced bindings form the environment it gets injected into but where it was defined from. Your code does not work in standard Schemes like Racket (#!r5rs), Ikarus and Chicken. Not even Kawa as it documents it also [requires a transformer](https://www.gnu.org/software/kawa/Macros.html). How the binding should work is well demonstrated by my answer – Sylwester Apr 11 '20 at 15:59
  • 1
    @jcubic: your examples *don't* 'work in any implementation which has quasiquote'. They work in implementations which have traditional Lisp macros, only. I don't think RnRS Scheme has had those, for any value of n. –  Apr 11 '20 at 18:35