Ran123
Ran123

Reputation: 85

SBCL Unbound variable in a macro

Im trying to create a macro that checks for the function definition that it gets passed in. I am new in lisp and I got stuck at checking whether or not defun is the first element in the list

(defmacro check (fun-param)
    (first fun-param)
)

(format t "~a~%" (check '(defun my-function (x y))))

From this I was expecting to get 'defun' but I get quote is unbound error. I then used eval inside the first to evaluate it but I got a "defun" is unbound error. I tried many steps like checking if the parameter is a list with (listp fun-param).

What I want to do is check if the first element is "defun" I tried (if (equal (first fun-param) "defun")) but it does not work either so I would like some guidance on what is going on. The way I am calling the check macro is the way I am being asked to call it so that is to be unchanged.

Upvotes: 1

Views: 203

Answers (2)

Rainer Joswig
Rainer Joswig

Reputation: 139241

Macro or Function?

Typically when we have a piece of code (as data) and what to check syntactic correctness (like it being a list which begins with a defun), then we can write a function.

(check '(defun my-function (x y))

Above most definitely should be a function. The code is quoted data and we can call the function to return a result.

Also the name of the function, here check, should indicate an action.

Rule: choose a function

  • data + runtime result -> write a function

Slightly different it is when we want to make it a macro to transform the passed code (which expands into more code) and we want to make sure that the passed code is well formed.

Rule: choose a macro

  • code transformation + syntactic check at compile time -> write a macro

A macro form would not need a quoted form, just the form itself is sufficient.

If we write a macro, then we also need to think about the naming. It's not a function and it will usually have different purposes: to define something (DEFVAR, DEFCLASS, ...), to set up a scope (WITH-OPEN-FILE, ...), to provide a new control structure (LOOP, DOLIST, ...), ...

Example

Let's say you want to check the form at macro expansion time. Thus we check if the form is a list and if the first element is defun.

Since defun is a symbol, we need to compare it to the symbol defun, not to the string "defun". Symbols are not strings and strings are not symbols. Identifiers in Lisp source code are symbols, not strings.

We then also return nil as the macro expanded form to execute, since this is only a simplified example. nil then evaluates to nil.

(defmacro checking (fun-param)
  (cond ((not (listp fun-param))
         (error "~a is not a list" fun-param))
        ((not (eq (first fun-param) 'defun))
         (error "DEFUN is missing in ~a" fun-param)))
  nil)

Thus:

CL-USER 27 > (checking (defun my-function (x y)))
NIL

CL-USER 28 > (checking (foobar my-function (x y)))

Error: DEFUN is missing in (FOOBAR MY-FUNCTION (X Y))
  1 (abort) Return to top loop level 0.

Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.

CL-USER 29 : 1 > 

Upvotes: 4

Barmar
Barmar

Reputation: 780655

Macros are supposed to return new code to be executed. Use backtick to return code with a template.

(defmacro check (fun-param)
  `(first ,fun-param)
)

Your code is calling first at compile time and returning quote, since it's receiving (quote (defun ...)) as its argument. Then it tries to use quote as a variable in the format argument.

Upvotes: 1

Related Questions