Question

This R5RS macro is what I have tried and is pretty much what I want to do. Racket or other implementations don't like this macro exactly where I wanted the magic to happen.

(define-syntax quote-unique
  (syntax-rules (magic end)

    ;; end case
    ((quote-unique magic processed end)
     'processed)

    ;; finished iteration
    ((quote-unique magic (processed ...) sym1 end rest ... )
     (quote-unique magic (processed ... sym1) rest ... end))

    ;; match (doesn't work since racket doesn't like sym1 twice in template)
    ;; but I'm looking for the same expression twice
    ((quote-unique magic processed sym1 sym1 . rest )
     (quote-unique magic processed sym1 . rest))

    ;; rotate
    ((quote-unique magic processed sym1 sym2 rest ... )
     (quote-unique magic processed sym1 rest ... sym2))

    ;; start iteration
    ((quote-unique rest ...)
     (quote-unique magic () rest ... end))))

This would have been easy in Common Lisp:

(defmacro quote-unique ( &rest xs )
  (labels ((remove-duplicates (lis)
        (if lis
        (if (member (car lis) (cdr lis))
                (remove-duplicates (cdr lis))
            (cons (car lis) (remove-duplicates (cdr lis)))))))

     (list 'quote (remove-duplicates xs))))

I also have been reading Define syntax primer and think the implementation of is-eqv? would have pointed me in the right directions, but it seems it's not a macro that is defined there.

If it's not possible in R5RS compile time, how could this be done with R6RS?

Was it helpful?

Solution 2

You can't do this using syntax-rules, but you can do it using syntax-case, by using a guard that uses free-identifier=?. Here's an example:

(define-syntax (remove-id stx)
  (syntax-case stx ()
    ((_ head ())
     #''())
    ((_ head (next tail ...)) (free-identifier=? #'head #'next)
     #'(remove-id head (tail ...)))
    ((_ head (next tail ...))
     #'(cons 'next (remove-id head (tail ...))))))

> (remove-id foo (foo bar baz qux foo bar))
; => (bar baz qux bar)

But of course, if you're going to use syntax-case, there's a much simpler way to implement your quote-unique (this implementation uses Racket's custom hashtables):

(require (for-syntax racket/dict))
(define-syntax (quote-unique stx)
  (define (id-dict ids)
    (foldl (lambda (id d)
             (dict-set d id #t))
           (make-immutable-custom-hash free-identifier=? (compose eq-hash-code syntax-e))
           (syntax-e ids)))
  (syntax-case stx ()
    ((_ ids ...)
     (with-syntax ((unique (dict-keys (id-dict #'(ids ...)))))
       #''unique))))

OTHER TIPS

The remove-id example of Chris Jester-Young's answer is expressible in R5RS:

(define-syntax remove-id
  (syntax-rules ()
    ((remove-id s (t ...))
     (letrec-syntax ((aux (syntax-rules (s)
                            ((aux p* ())
                             'p*)
                            ((aux p* (s . rest))
                             (aux p* rest))
                            ((aux (p (... ...)) (u . rest))
                             (aux (p (... ...) u) rest)))))
       (aux () (t ...))))))

(Note that quoting the ellipsis by (... ...) is not strictly R5RS (only R7RS), but is only used to have the resulting sequence in the given order and not reversed. Thus by adding another macro, you can even ditch the ellipsis.)

I hope this example makes it clear how to solve your original problem. If something can be solved with hygienic macros, one should think twice before using a procedural macro or a macro facility that probably won't be standardized after R6RS.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top