Search code examples
macroscommon-lisplisp-2

Common Lisp Lisp-1 macro


I am trying to emulate the single namespace of scheme within common lisp, with a macro (based on Doug Hoyte's) that expands to a lambda, where every use of an f! symbol (similar to Doug Hoyte's o! and g! symbols) in the function position expands to the same expression, but with funcall added in the function position of each invocation. For example:

(fplambda (f!z x) (f!z x x))

would expand to:

(LAMBDA (F!Z X) (FUNCALL F!Z X X))

The macro currently looks like this:

(defmacro fplambda (parms &body body)
  (let ((syms (remove-duplicates
                (remove-if-not #'f!-symbol-p
                               (flatten body)))))
    `(lambda ,parms
       (macrolet ,(mapcar
               (lambda (f)
                 `(,f (&rest parmlist)  `(funcall ,',f ',@parmlist)))
               syms))
         ,@body)))

but given the above input, it expands (as far as I can see) to this:

(LAMBDA (F!F X)
       (MACROLET ((F!F (&REST PARMLIST) `(FUNCALL ,'F!F ',@PARMLIST))))
       (F!F X X))

In the macrolet definition, F!F should not be quoted or unquoted, and parmlist should just be unquoted. What is going on? Thanks in advance!


Solution

  • Your definition is mostly right. You just made two pretty simple mistakes. The first one being a mismatched paren. The macrolet does not include the body (in the output the macrolet and the body are at the same level of indentation).

    As for the nested backquote, the only mistake is the quote before parmlist. Other than that everything else is correct. The comma and quote before F!F is actually correct. From the hyperspec: "An implementation is free to interpret a backquoted form F1 as any form F2 that, when evaluated, will produce a result that is the same under equal as the result implied by the above definition". Since the inner backquote has not been expanded yet, it does not have to be free of quotes and unquotes. The expression `(,'x) is actually the same as `(x).

    Nested backquotes are notoriously complicated. What is probably the easiest way to understand them is to read Steele's explanation of them.

    Edit:

    The answer to your question about whether it is possible to use a fplambda expression in the function position is no. From the part of the hyperspec that deals with the evaluation of code: "If the car of the compound form is not a symbol, then that car must be a lambda expression, in which case the compound form is a lambda form.". Since the car of the form, (fplambda ...), is not a lambda expression, your code is no longer valid Common Lisp code.

    There is a workaround to this that I figured out, but it's kind of ugly. You can define a reader macro that will allow you to write something like ([fplambda ...] ...) and have it read as

    ((LAMBDA (&REST #:G1030) (APPLY (FPLAMBDA ...) #:G1030)) ...)
    

    which would do what you want. Here is code that will allow you to do that:

    (set-macro-character #\[ 'bracket-reader)
    (set-macro-character #\] (get-macro-character #\)))
    
    (defun bracket-reader (stream char)
      "Read in a bracket."
      (declare (ignore char))
      (let ((gargs (gensym)))
        `(lambda (&rest ,gargs) 
           (apply ,(read-delimited-list #\] stream t)
                  ,gargs))))
    

    The only other solution I can think of would be to use some sort of code walker (I can't help you there).