2

I am a beginner lisp programmer, and I'm following the Practical Common Lisp book, specifically chapter 9.

After finishing the chapter, I've tried to expand the unit-testing environment. Specifically, I wanted a macro that gets a function name and a list of inputs and outputs instead of manually comparing them. i.e.

(check-i-o 1+ (2 3) (3 4)) => (check (= (1+ 2) 3) (= (1+ 3) 4))

I will admit that I am still a bit confused about the syntax of backtiks, and this probably is the problem, but this is the following code I wrote trying to program this

(defmacro check-i-o (function &body inputs-outputs)
  `(check
    ,@(loop for i-o in inputs-outputs
            collect `(= (,function (first i-o)) (second i-o)))))

For some reason, whenever I try to run this macro on an example (for instance, (check-i-o 1+ (2 3) (3 4))) I encounter the error The variable I-O is unbound.

If it is relevant: I am using slime-repl sbcl in portacle emacs on windows.

Thank you so much for your help!

I have tried multiple variations on the code provided (mainly changing backticks, using #'1+ instead of 1+ when calling the macro, and trying to remove the &body from the decleration of the macro...) but nothing helped and I am at a loss...

1 Answer 1

3

You can macroexpand your example code to debug your problem:

> (macroexpand '(check-i-o 1+ (2 3) (3 4)))

(CHECK 
  (= (1+ (FIRST I-O)) (SECOND I-O)) 
  (= (1+ (FIRST I-O)) (SECOND I-O)))

The generated code contains references to I-O, a variable that is not bound when you evaluate the expression. It was a variable bound inside your macro, but you directly inserted the I-O symbol in the resulting expression. That's why you have an error.

The backquote/unquote mechanism is a way to produce a form where some parts are evaluated and other parts aren't: what is backquoted is not evaluated, except for the subterms which are unquoted.

In your code, you write:

`(= (,function (first i-o)) (second i-o)))

Only the content of the function variable is inserted in the resulting form. The rest is left unevaluated. You need to write:

`(= (,function ,(first i-o)) ,(second i-o)))

Another improvement would be to use the fact that LOOP allows you to pattern-match (aka. destructure) lists for you:

,@(loop
    for (i o) in inputs-outputs
    collect `(= (,function ,i) ,o))))
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.