Why do we need funcall in Lisp?

13.3k Views Asked by At

Why do we have to use funcall to call higher order functions in Common Lisp? For example, why do we have to use:

(defun foo (test-func args)
  (funcall test-func args))

instead of the simpler:

(defun bar (test-func args)
  (test-func args))

Coming from a procedural background, I'm a bit surprised by that since the languages I'm more used to (e.g. Python, C#) don't need the distinction. In particular, on the source level at least, the C# compiler transforms it to something like func.invoke().

The only problem I see is that this would mean we couldn't call a global function test-func anymore because it'd be shadowed, but that's hardly a problem.

3

There are 3 best solutions below

0
On BEST ANSWER

Strictly speaking, funcall would not be needed, but there are some lisps (lisp-2 variants, such as Common Lisp) that separate the variable name space of the function name space. Lisp-1 variants (e.g. Scheme) do not make this distinction.

More specifically, in your case, test-func is in the variable name space.

(defun foo (test-func args)
  (funcall test-func args))

Therefore you need a construct that actually searches the function object associated with this variable in the variable name space. In Common Lisp this construct is funcall.

See also this answer.

1
On

The majority of Lisps have two namespaces (functions and variables). A name is looked up in the function namespace when it appears as the first element in an S-expression, and in the variable namespace otherwise. This allows you to name your variables without worrying about whether they shadow functions: so you can name your variable list instead of having to mangle it into lst.

However, this means that when you store a function in a variable, you can't call it normally:

(setq list #'+) ; updates list in the variable namespace
(list 1 2 3) => (1 2 3) ; looks up list in the function namespace

Hence the need for funcall and apply:

(funcall list 1 2 3) => 6 ; looks up list in the variable namespace

(Not all Lisps have two namespaces: Scheme is an example of a Lisp with just one namespace.)

1
On

In Common Lisp, each symbol can be associated with its symbol-function and its symbol-value, among other things. When reading a list, by default, Common Lisp interprets:

  • arg1 as a function and so retrieves test-func's symbol-function, which is undefined -- thus function bar doesn't work
  • arg2 as something to be evaled -- thus function foo retrieves test-func's symbol-value, which, in your case, happens to be a function