Search code examples
lambdamacroslispcommon-lispclisp

Creating a Lisp macro that can apply a lambda to a list within a context


Basically I am trying to write a Common Lisp macro defined as:

(defmacro applyfunct (function arguments variables))

that applies the function given as the argument function, to the argument arguments (which is a list of arguments to apply the function to), that, when necessary, uses the variables given to it in the list of lists variables. So the return value when it is called with these arguments like this:

(applyfunct + (7 5) ((x 1) (y 2)))

would be 12, considering that 7+5=12, and the context variable x and y were not needed to apply the function to the arguments. However when it does require the context variables given:

(applyfunct (lambda (x y) (+ (* a x) (* y b)) (4 2) ((a 2) (b 4))))

it should use these variables if they are needed in the function given to evaluate to return 16, because:

(applyfunct (lambda (x y) (+ (* a x) (* y b)) (4 2) ((a 8) (b 1))))
;                    4 2        8 4     2 1    
; (+ (* 8 4) (* 2 1)) => 34

hopefully my comments here make clear what I'm trying to do. What I have so far is:

(defmacro applyfunct (function arguments variables)
    (let ( ((car (first contents)) (cdar (first contents))
           ((car (second contents)) (cdar (second contents))

but I have no idea how to proceed...

(apply function arguments) 

would only work for the first example call where the function is +, not the second function call with the lambda. Am I missing something here? Should I be using ` or #' somehow? Note: I am trying to program as functionally as possible (minimal to no side effects, for example, not using setq). I am also using the CLISP implementation of Common Lisp.


Solution

  • As far as I know, apply would only work for the second example (lambda), not +.

    But remember that you're writing a macro, which can construct program code (such as (+ 7 5) or ((lambda (x y) ...) 4 2)) as needed.

    The first step is to make the function call work (ignoring variables for now). In Lisp, the syntactic form of a function call is a list whose first element (head) is the function and and whose remaining elements (tail) are the function arguments. This structure can be built e.g. by using cons:

    (defmacro applyfunct (function arguments variables)
        (cons function arguments))
    

    Or, using the syntactic sugar of `:

    (defmacro applyfunct (function arguments variables)
        `(,function ,@arguments))
    

    (` acts like a code template, with , marking the spots where variables are to be inserted and ,@ additionally flattening the list.)

    Now, to make variables work, let can be used to provide bindings (as in your code). However, there is no need to destructure variables manually; it already has the right shape for a list of let bindings:

    (defmacro applyfunct (function arguments variables)
        `(let ,variables
            (,function ,@arguments)))
    

    We can test this macro:

    (print (macroexpand-1 '(applyfunct (lambda (x y) (+ (* a x) (* y b))) (4 2) ((a 8) (b 1)))))
    ; by the way, one of the ') is misplaced in your example: ----------^
    

    Producing this output:

    (LET ((A 8) (B 1)) ((LAMBDA (X Y) (+ (* A X) (* Y B))) 4 2))
    

    ... which is exactly what we want.