Search code examples
macrosschemerackethygiene

Writing myletstar macro (hygiene) in Scheme


I am trying to re-write let* hygiene macro, I have it as normal macro and I would like to have it as hygiene macro if it's possible. I don't have too much experience with this macro types. So I would really appreciate help. Also my another representation of working let* macro is not working, with same error as hygiene macro.

Working let*

(define-macro let*1
(lambda (assgn . body)
(let ((loop (gensym))
      (lst (gensym)))
  (let loop ((lst assgn))
     (if (null? lst)
         `(begin ,@body)
         `((lambda (,(caar lst))
             ,(loop (cdr lst)))
           ,(cadar lst)))))))

Not working hygiene let* -> error: lambda: not an identifier in: (caar lst)

(define-syntax let*2
(syntax-rules ()
((let*2 (set ...) body ...)
 (let loop ((lst '(set ...)))
   (if (null? lst)
       body ...
       ((lambda ((caar lst))
          (loop (cdr lst)))
        (cadar lst) 1))))))

Not working let*, but also having same error as the second one.

(define-macro let*3
(lambda (assgn . body)
(let ((loop (gensym))
      (lst (gensym)))
  `(let ,loop ((,lst assgn))
     (if (null? ,lst)
         (begin ,@body)
         ((lambda ((caar ,lst))
             (,loop (cdr ,lst)))
           (cadar ,lst)))))))

Apologize for little confusing question, I am stucked with this problem for some time already and caffeine isn't usefull anymore.

Some tests (I picked symbol names to test symbol-capture(I know I didn't had to)):

(let*1 ((body 10)
   (lst (+ body 1)))
  (list body lst))

(let*2 ((body 10)
    (lst (+ body 1)))
  (list body lst))

(let*3 ((body 10)
    (lst (+ body 1)))
  (list body lst))

Edit: Question is answered, added solution without using let by editing Lief Andersen code

(define-syntax let*
  (syntax-rules ()
    ((let*2 ([x1 e1][x2 e2] ...)body ...)
     ((lambda  (x1)
        (let* ([x2 e2] ...)
           body ...))
         e1))))

Solution

  • Your second one is the closest. A hygenic let* macro can be written with define-syntax-rule. (If you're writing this in an implementation of scheme rather than racket, you can also just compose define-syntax and syntax-rule for the same effect.)

    (define-syntax-rule (let* ([x1 e1]
                               [x2 e2] ...)
                          body ...)
      (let ([x1 e1])
        (let* ([x2 e2] ...)
          body ...))
    

    And if you're feeling really pedantic, you can make a hygenic let macro:

    (define-syntax-rule (let ([x e] ...)
                          body ...)
      ((lambda (x ...) body ...) e ...))