jcubic
jcubic

Reputation: 66490

How to insert literal identifier from input pattern as symbol in syntax-rules macro

I have code like this:

(define-syntax macron
  (syntax-rules ()
    ((_ name)
     (lambda (x)
       (eval (cons 'name x) (interaction-environment))))))

(define x (map (macron lambda)
               '(((x) (display x)) ((a b) (+ a b)))))

(let ((square (car x)) 
      (sum (cadr x))) 
  (display (square 10)) 
  (newline) 
  (display (sum 1 2 3)) 
  (newline))

the code is working it use macro as value by wrapping it with lambda. My question is how can I put inside syntax-rule macro literal symbol 'name instead of (cons 'lambda ...) so the output code is:

(lambda (x)
  (eval (cons 'name x) (interaction-environment)))

so it work with code like this:

(define (name x)
  (display x)
  (newline))

(for-each (macron lambda) ;; lambda can be anything
          '((1) (2) (3)))

and it print all the numbers.

I know that I can change the name in pattern into something else, but I want to know more about syntax-rules and it's edge cases. So is it possible to have name if I use it as input pattern?

I'm looking for answers with R7RS, that have more of this type of edge cases covered.

Upvotes: 0

Views: 154

Answers (1)

Sylwester
Sylwester

Reputation: 48745

All macros happens in compile time so runtime stuff might not exist. That means that you should think of it as syntax sugar and use it as susch. eg.

(for-each (macron something) '((1) (2) (3)))

Should then have an expansion based on that. Your current expansion is that it turns into this:

(for-each (lambda (x)
            (eval (cons 'someting x) (interaction-environment))
          '((1) (2) (3)))

For something being a macro this will apply the macro in runtime. It is bad. It also removes the need for the macro in the first place. You could do this instead:

(define (macron-proc name)
  (lambda (x)
    (eval (cons name x) (interaction-environment))))

(for-each (macron-proc 'something) '((1) (2) (3)))

I made a programming language that had passable macros:

(define xor (flambda (a b) `(if ,a (not ,b) ,b)))
(define (fold comb init lst)
  (if (null? lst)
      init
      (fold comb (comb (car lst) init) (cdr lst))))
(fold xor #f '(#t #t)) ; ==> #f

It's not a very good approach if you are targeting an efficient compiled end product. The first macros were indeed like this and they removed it in LISP 1.5 before Common Lisp. Scheme avoided macros for many years and opted for syntax-rules in R4RS as an optional feature. R6RS is the only version that has full power macros.

With a procedure instead of macros this is actually the same as the following code with the bad eval removed:

(for-each (lambda (x)
            (apply something x))
          '((1) (2) (3)))

Which means you can implement macron much easier:

(define-syntax macron
  (syntax-rules ()
    ((_ name)
     (lambda (x)
       (apply name x)))))

But from looking at this now you don't need a macro at all. This is partial application.

(define (partial proc arg)
  (lambda (lst)
    (apply proc arh lst)))

(map (partial + 3) '((1 2) (3 4) (4 5)))
; ==> (6 10 12)

There is actually a SRFI-26 called cut/cute which allows us to do something similar where it wraps it in a lambda:

(map (cut apply + 3 <>) '((1 2) (3 4) (4 5)))

The syntax-rules are the macros with the least power. You cannot do anything unhygienic and you cannot make new identifiers based on other ones. Eg. it' impossible to implement a racket style struct where you can do (struct complex [real imag]) and have the macro create complex?, complex-real, and complex-imag as procedures. You need to do as SRFI-57 does and require th euser to specify all the names such that you don't need to concatenate to new identifiers.

Right now R7RS-small only has syntax-rules. I think it was a mistake not to have a more powerful macro as an alternative since now the R7RS-large cannot be implemented with R7RS-small.

Upvotes: 2

Related Questions