Why must I funcall a function returned from anothe

2019-02-15 06:11发布

问题:

Why doesn't this work?

( ((lambda () (lambda (x) (funcall #'1+ x)))) 2)
 ; yields Compile-time error: illegal function call

I ran into a situation like this and it later turned out that a funcall fixes it, i.e.

(funcall ((lambda () (lambda (x) (funcall #'1+ x)))) 2) ; => 3

I'm confused because it seems like the first one should work, because I actually have a function I'm calling, not just a symbol that may belong to either namespace (i.e. (type-of ((lambda () #'1+))) ; => FUNCTION). I thought it would be kind of like how you don't need to funcall a lambda for example, e.g.((lambda (x) x) :HI) ; => :HI. What am I missing?

回答1:

The syntax of Common Lisp requires that, everytime you want to call a function through a compund form of the type:

(f a1 a2 ... an)

the first element of the list, f, must be a symbol denoting a function name, or a list denoting a lambda expression, i.e. (see the manual):

lambda expression n. a list which can be used in place of a function name in certain contexts to denote a function by directly describing its behavior rather than indirectly by referring to the name of an established function; its name derives from the fact that its first element is the symbol lambda.

So, this basically means that you cannot have as first element any expression that returns a function as value. In those cases, you must use funcall.

So, in your second example, the first argument of the funcall is ((lambda () (lambda (x) (funcall #'1+ x)))), which is a correct coumpound form, in which the first element of the list is the lambda expression (lambda () (lambda (x) (funcall #'1+ x))) (applied to an empty list of arguments).

In the first example, instead, you have as first element of the list an expression returning a function, so that you must use funcall.



回答2:

Common Lisp uses the word form for everything which can be evaluated. A form is either

  • a symbol like foo
  • a compound form, a list, see below
  • or a self-evaluating object (like numbers, characters, arrays, strings, ...).

A compound form is either

  • a special form (<special-operator> ...)
  • a lambda form like (lambda (...) ...)
  • a macro form (<macroname> ...)
  • or a function form (<functionname> ...).

Above is the set of compound forms. The ANSI Common Lisp specification provides no way to add a new type of forms or a different syntax. The interface of what forms the functions like EVAL or COMPILE accept is not extensible.

So something like

(((lambda (foo)
    (lambda (bar)
      (list foo bar)))
  1)
 2)

is not valid Common Lisp. This is not meaningful in Common Lisp:

( <not a lambda form,
   not a special operator,
   not a macro name
   and not a function name>
2)

Note that Common Lisp allows lambda forms, special operators, macro names and function names as the first element in a compound form. But it does not allow variables and it does not allow other compound forms as the first element in a compound form.

Means this is not meaningful in Common Lisp:

( <a function form> 2)

Thus ((foo 1) 2) or (((foo 1) 2) 3) or ((((foo 1) 2) 3) 4) or (((((foo 1) 2) 3) 4) 5) is not legal in Common Lisp. You get the idea. To call function objects returned from function calls, we have to use (funcall (foo ...) ...). This makes calling returned function objects more obvious than just ((foo ...) ...).

Let's praise the designers of Common Lisp for this feature. Otherwise I might have to look at possibly meaningful code beginning with

(((((((((((( .....

and it would be very hard to figure out what it does. Basically that would be write-only code.

Your question:

Why must I funcall a function returned from another?

The short answer: because the syntax does not allow other ways, in Common Lisp.



回答3:

I have read this article and then modified my code below:

(defparameter *my-fun* 1)

(defun my-func (v0)   
  (setf (symbol-function '*my-fun*)
    (lambda (v1)
      (+ v0 v1)))   
   '*my-fun*)

And call it in this way ((my-func 2) 3),but it also report "illegal function call". I think my code is keeping with lambda calculus,but where is wrong.

In my opinion, (my-func 2) returns symbol *my_fun*, and the function cell of *my-fun* points to a function object, so ((my-func 2) 3) => (*my-fun* 3) => ((lambda (v1) (+ 2 v1)) 3) => (+ 2 3) => 5



回答4:

This works: First your identical definitions:

(defparameter *my-fun* 1)

(defun my-func (v0)   
  (setf (symbol-function '*my-fun*)
    (lambda (v1)
      (+ v0 v1)))   
   '*my-fun*)

However, invoke using funcall:

(funcall (my-func 2) 3)