Common Lisp, "defined but never used"

823 Views Asked by At

This function compiles with warnings, fn is defined and never used in the first line, and that fn is an undefined function in the second line:

(defun test-function (fn)
  (funcall #'fn))

Why? A general explanation or a link to it would be great.

PD: Complete log:

test.lisp:9:1:                                                                             
  style-warning:                                                                           
    The variable FN is defined but never used.                                             
    --> PROGN SB-IMPL::%DEFUN SB-IMPL::%DEFUN SB-INT:NAMED-LAMBDA                          
    ==>                                                                                    
      #'(SB-INT:NAMED-LAMBDA TEST-FUNCTION                                                          
            (FN)                                                                           
          (BLOCK TEST-FUNCTION (FUNCALL #'FN)))                                                     


test.lisp:10:3:                                                                            
  style-warning:                                                                           
    undefined function: FN                                                                 
    ==>                                                                                    
      (SB-C::%FUNCALL #'FN)
2

There are 2 best solutions below

8
On BEST ANSWER

If you want to call a function passed as parameter, or assigned to a variable, simply use the variable or parameter as first argument to funcall:

(defun test-function(fn)
  (funcall fn))

(test-function #'+)
;; => 0

The notation #'X is an abbreviation for (function X), (see the manual), where X must be the name of a function, for instance defined with a defun or labels or flet, or a lambda expression. So, #'fn does not work since fn is not the name of a function, but a variable (in this case, a parameter).

Common-Lisp is a Lisp-2, that is the namespace of functions is different from that of other variables. So the names of the functions are specials in the sense that you can call them directly in a form, while, if a function is assigned to a variable, it must be called with (funcall name-of-the-variable arguments).

1
On

This function compiles with warnings

Note that these are only a warnings:

CL-USER> (defun test-function (fn)
           (funcall #'fn))
  1. the variable FN is not used.
  2. the function FN is undefined.

Let's look at the function:

(defun test-function (fn)   ; this introduces a variable FN
  (funcall #'fn))           ; here you use a function FN

Since there is no local function FN in scope, you are using a global function FN. In your case it is not defined.

You can define a global function FN later:

CL-USER> (defun fn ()
          'foobar)
FN

This would already work, since Common Lisp usually also uses late binding for undefined functions and would look up the function at runtime.

If we compile your original function again, then you can see that only one warning remains:

CL-USER> (defun test-function (fn)     ; the variable FN is defined
           (funcall #'fn))             ; the function FN is used

;   The variable FN is defined but never used.

This is because we now have a global function FN defined.

But: calling a global function is probably not what you wanted, because that could be written easier as:

(defun test-function (fn)
  (fn))   ; calling the function `FN`.

FUNCALL is for calling function objects with arguments:

Typical use cases of FUNCALL are calling function objects with arguments:

(funcall foo 1 2 3)

Where FOO is a variable bound to a function.

In your case this is probably intended:

CL-USER> (defun test-function (fn)      ; a variable FN gets introduced
           (funcall fn))                ; a variable FN gets used

Remember: (funcall #'foo ...) looks wrong.

If you have something like (funcall #'foo 1 2 3) in your code, then you are probably doing something wrong, since it can be easier written as (foo 1 2 3).

Thus it is a code smell to use (funcall #'foo 1 2 3), indicating that you probably wanted to call a function object, but you actually are calling a function via its name.