2

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)
Carlos Ledesma
  • 307
  • 1
  • 2
  • 8

2 Answers2

7

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).

Renzo
  • 26,848
  • 5
  • 49
  • 61
  • Yes, I know this is how it should be, but I' trying to understand why my proposal does not work. I'm missing something, definitely. For me, the function I provided should compile, and calling it like this should work: (test-function '+) Thank you very much for your answer. – Carlos Ledesma Sep 25 '16 at 20:58
  • 1
    @CarlosLedesma, I've updated the answer with the reason for which `#'fn` does not work. – Renzo Sep 25 '16 at 21:03
  • So there is no other way to pass a function as an argument to a function, other than passing the function value (I mean, the one which #' returns)? – Carlos Ledesma Sep 25 '16 at 21:08
  • 1
    You must pass a function object, that you can access in several ways: a) with `(function function-name)` (or `#'function-name` which is an abbreviation of the previous form;) b) with `(symbol-function 'function-name)` (note the quote); c) with `(fdefinition function-name)` if `function-name` is globally defined. You can also pass directly an anonymous lambda expression, like `(lambda (x) (+ x x x))`. – Renzo Sep 25 '16 at 21:14
  • 1
    If OP wants `(test-function '+)` to work, am I correct in thinking that `(defun test-function (fn) (funcall (symbol-function fn)))` would achieve this? – zwol Sep 25 '16 at 21:22
  • 1
    @zwol, yes, you are correct. The reason is that in this case the *variable* `fn` is bound to the *symbol* `+`, so `(symbol-function fn)` is actually equal to `(symbol-function '+)`, and `+` is a symbol that is `fbound` to a function. – Renzo Sep 25 '16 at 21:25
  • 2
    @zwol You don't actually need to use `SYMBOL-FUNCTION` there. `FUNCALL` will do that itself if called with a symbol: `(funcall '+ 2 2)` => `4`. The difference between `(test-function '+)` and `(test-function #'+)` is just that the former calls the current function named by `+`, while the latter calls the function that was named by `+` at the time the call was evaluated. In this case the result should be the same since the function is called immediately, but if the function was stored somewhere to be called later, it might be redefined in the meanwhile. – jkiiski Sep 26 '16 at 04:08
  • Thank you very much for these clarifications. Actually the question asked by @zwolf helped me a lot, when answered. – Carlos Ledesma Sep 26 '16 at 12:46
4

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.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
  • Thank you very much! The code smells, yes, but it's just a toy example. The idea was to understand why it happens, as I'm learning Common Lisp at the moment. And after a few hours trying, I was not able to arrive to a clear conclusion. – Carlos Ledesma Sep 26 '16 at 12:33