Search code examples
lispcommon-lispelispquote

Backquote expansion in Lisp


I'm a Lisp beginner and I'm struggling to understand why the following code gives me an error.

(dolist (elem '(mapcar
                mapcon))
  (when (fboundp `',elem) (print "hello")))

Thanks.

Edit: A bit more context. I wrote the following in Elisp and I don't know how to fix it.

(dolist (ui-elem '(menu-bar-mode
                   tool-bar-mode
                   tooltip-mode
                   scroll-bar-mode
                   horizontal-scroll-bar-mode))
  (when (fboundp `',ui-elem) (ui-elem -1)))

Solution

  • Note

    In your question you mix common-lisp and elisp, but they are two different languages. The question however touches on concepts that are identical in both languages.

    The need to quote symbols

    The code you want to write checks if a symbol is bound to a function. What you already know probably is that you can call fboundp on a symbol to determines this:

    (fboundp 'menu-bar-mode)
    => t
    

    When you evalute the above form, 'menu-bar-mode is the same as (quote menu-bar-mode), and is evaluated as the symbol object menu-bar-mode. This is the value that is given as an argument to fboundp.

    In you example you want to iterate over a list of symbols, call fboundp on it and call the function if the symbol denotes a function. You can do this as follows:

    (dolist (s '(menu-bar-mode and other symbols))
      (when (fboundp s)
        (funcall s -1)))
    

    The list of symbols '(menu-bar-mode and other symbols) is quoted, which means that when dolist evaluates it, it sees a list of symbols. The value to which s is bound at each iteration of the loop is a symbol object, there is no need to quote them.

    Quoting a symbol is something you have to do when writing them in your code so that they are not interpreted as variables. When you iterate over a list of symbols, you already manipulate symbols.

    Note also that both Common Lisp and Emacs Lisp are "Lisp-2", meanings that you have to use (funcall ui-elem -1) instead of writing (ui-elem -1). When you write the latter form, that means calling the function literally named ui-elem because for function application, the first symbol in the list is not evaluated, it is taken literally.

    Too many levels of quoting

    The actual error I have when I execute your code is:

    (wrong-type-argument symbolp 'mapcar)
    

    It may look like 'mapcar denotes a symbol, because when you want the interpreter to evaluate some code as a symbol, you need to quote it. However, Lisp printers write objects in a way that they can be read back to "similar" objects. The error message that is printed if I expect a symbol to be a number is the following, where symbol foo is printed unquoted:

    (+ 'foo 3)
    
    ;; error: (wrong-type-argument number-or-marker-p foo)
    

    In your error message, the form that you are trying to use as a symbol is (quote mapcar). Recall that when you directly call fboundp:

    (fboundp 'mapcar)
    

    It is the same as-if you wrote:

    (fboundp (quote mapcar))
    

    First, (quote mapcar) is evaluated, as the symbol mapcar. Then, fboundp is applied to that value.

    But when you write the following, while ui-elem is bound to symbol mapcar:

    (fboundp `',ui-elem)
    

    This is equivalent to:

    (fboundp `(quote ,ui-elem))
    

    The argument to fboundp is evaluated as (quote mapcar). You have one extra level of quoting. You could write instead:

    (fboundp `,ui-elem)
    

    But then, you don't need to use backquote/comma, you can directly write:

    (fboundp ui-elem)