Search code examples
schemecommon-lispsyntax-rules

Is it possible to define a syntax-rules macro in Common Lisp


I learned that it is easy to define a small macro in scheme by syntax-rules. Is it possible to define a syntax-rules macro which will return a list can be read by defmacro in Common Lisp?

Which might work like this:

(defmacro and (&rest rest)
  (syntax-rules (rest) ()
    ((_)         t)
    ((_ x)       x
    ((_ x y)     (if x (if y y nil) nil))
    ((_ x y ...) (if x (and y ...) nil))))

Solution

  • Tim Bradshaw's destructuring-match is intended to make this kind of pattern matching easy in macros: it is a combination of case and destructuring-bind, so the key of each clause has the syntax of a destructuring lambda list. There are several other pattern matching systems for CL, but this one explicitly is aimed at writing macros: it's not a general data structure matching tool, just a generalised version of destructuring-bind.

    It comes with a little example macro (itself written using destructuring-match as well as expanding into something which uses destructuring-match!) called define-matching-macro, in which you can write a version of and, here called et. I think the following two macros are correct but I have not thought much about them.

    (define-matching-macro et
      "This is meant to be AND"
      ((_) 't)
      ((_ x) x)
      ((_ x . more ) `(if ,x (et ,@more) nil)))
    

    You can alternatively just use destructuring-match in an ordinary defmacro macro. Here I am not using &whole to get the whole form so we don't have to bother with the car of the form:

    (defmacro et (&rest forms)
      "This is also meant to be AND"
      (destructuring-match forms
        (() 't)
        ((x) x)
        ((x . more) `(if ,x (et ,@more) nil))))
    

    Notes:

    • destructuring-match uses symbols whose name is "_" as blanks the same way Scheme does, so you don't need to declare them ignored, they are all distinct from each other, and in fact you may not refer to them in the body of the clause; but it does not do Scheme's x ... thing at all, so you just need to use dotted lambda lists or &rest or whatever you prefer;
    • these are not hygienic macros, they're just pattern-matching macros;
    • These are somewhat simpler than your example, but I think they are enough;
    • your macro is not in fact safe, because (if x x nil), as well as being redundant (it's just x) may evaluate x twice, which is unsafe.