I think it's a very good article. Unfortunately, I'm having problems with some lisp code under the "function objects" item. The author is trying to show how you can return a raw list that later may be interpreted as a function call. Here's the relevant code:
(defun f (op i) (funcall op i))
(defun addn (n) (list 'lambda '(x) (list '+ 'n n)))
;;This is supposed to evaluate to 10 (f (addn 3) 7)
Now, the problem is that neither SBCL no CLISP would accept this. Maybe there's just a syntax error. But I couldn't fix it.
But even if the syntax error could be fixed, here's what I'm really baffled at: the return from (addn 3) will return an S expression (lambda (x) (+ n 3)). This s-expression needs to be evaluated before f can treat it as a function. Right? It feels like a call to "eval" is missing somewhere. I re-wrote it like this:
(f (eval (addn 3)) 7)
And it seems to work. But now I'm afraid that I'm missing something important. The call to eval (as I understand) will basically invoke the interpreter (or compiler for compiled code) to interpret (or compile) S-expression (lambda (x) (+ n 3)). But in author's original code, it looks like that S-expression can be understood directly, without interpreter's (or compiler's) help. If that were true, it would be truly amazing and I'd have to change some of my world-concepts ...
> I think it's a very good article. Unfortunately, I'm having problems > with some lisp code under the "function objects" item. > The author is trying to show how you can return a raw list that later > may be interpreted as a function call. Here's the relevant code:
> ;;This is supposed to evaluate to 10 > (f (addn 3) 7)
> Now, the problem is that neither SBCL no CLISP would accept this. > Maybe there's just a syntax error. But I couldn't fix it.
> But even if the syntax error could be fixed, here's what > I'm really baffled at: the return from (addn 3) will return an S > expression (lambda (x) (+ n 3)). This s-expression needs to be evaluated > before f can treat it as a function. Right? It feels like a call to > "eval" is missing somewhere. I re-wrote it like this:
> (f (eval (addn 3)) 7)
> And it seems to work. But now I'm afraid that I'm missing something > important. The call to eval (as I understand) will basically invoke the > interpreter (or compiler for compiled code) to interpret (or compile) > S-expression (lambda (x) (+ n 3)). But in author's original code, it > looks like that S-expression can be understood directly, without > interpreter's (or compiler's) help. If that were true, it would be truly > amazing and I'd have to change some of my world-concepts ...
But isn't this code different from the original? In your case function ff returns the result of evaluating S-expression (lambda (x) (+ x n)) which will be a function-form. In the original version, addn was returning the S-expression unevaluated. Someone has to evaluate it to construct a function object, right?
> But isn't this code different from the original? > In your case function ff returns the result of evaluating S-expression > (lambda (x) (+ x n)) which will be a function-form. In the original > version, addn was returning the S-expression unevaluated. Someone has to > evaluate it to construct a function object, right?
> Thanks, > Andy.
Yes, you're right, the code is a bit different. Instead of evaluating a function, it's trying to evaluate a CONS (list).
So, instead of creating a function, create a list: CL-USER> (defun addn (n) ;; (lambda (x) (+ x n))) (list 'lambda '(x) (list '+ 'x n))) ADDN CL-USER> (ff 'addn 7) (LAMBDA (X) (+ X 7)) CL-USER> (ff (addn 3) 7) ; Evaluation aborted on #<TYPE-ERROR #x1A7B00F6>. CL-USER> (type-of (addn 3)) CONS CL-USER> (ff (coerce (addn 3) 'function) 7) 10
Now it works, after coercing the list to a function.
> > But isn't this code different from the original? > > In your case function ff returns the result of evaluating S-expression > > (lambda (x) (+ x n)) which will be a function-form. In the original > > version, addn was returning the S-expression unevaluated. Someone has to > > evaluate it to construct a function object, right?
> > Thanks, > > Andy.
> Yes, you're right, the code is a bit different. Instead of evaluating > a function, it's trying to evaluate a CONS (list).
> Now it works, after coercing the list to a function.
Or you could make it a little more elegant and rewrite 'ff': CL-USER> (defun ff (op i) (funcall (if (typep op 'function) op (coerce op 'function)) i)) FF CL-USER> (ff (addn 3) 7) 10
>> But isn't this code different from the original? >> In your case function ff returns the result of evaluating S-expression >> (lambda (x) (+ x n)) which will be a function-form. In the original >> version, addn was returning the S-expression unevaluated. Someone has to >> evaluate it to construct a function object, right?
>> Thanks, >> Andy.
> Yes, you're right, the code is a bit different. Instead of evaluating > a function, it's trying to evaluate a CONS (list).
> >> But isn't this code different from the original? > >> In your case function ff returns the result of evaluating S-expression > >> (lambda (x) (+ x n)) which will be a function-form. In the original > >> version, addn was returning the S-expression unevaluated. Someone has to > >> evaluate it to construct a function object, right?
> >> Thanks, > >> Andy.
> > Yes, you're right, the code is a bit different. Instead of evaluating > > a function, it's trying to evaluate a CONS (list).
> > Now it works, after coercing the list to a function.
> Thanks for the reply. > Interesting, will "coercing" invoke an eval?
No, it just changes a list into something that can be evaluated by 'funcall'. 'funcall' is where most of the heavy lifting (evaluation) is being performed.
> On Oct 25, 7:23 pm, Andy Venikov<swojchelo...@gmail.com> wrote: >> On 10/25/2010 7:14 PM, vanekl wrote: >> <snip>
>>>> But isn't this code different from the original? >>>> In your case function ff returns the result of evaluating S-expression >>>> (lambda (x) (+ x n)) which will be a function-form. In the original >>>> version, addn was returning the S-expression unevaluated. Someone has to >>>> evaluate it to construct a function object, right?
>>>> Thanks, >>>> Andy.
>>> Yes, you're right, the code is a bit different. Instead of evaluating >>> a function, it's trying to evaluate a CONS (list).
>>> Now it works, after coercing the list to a function.
>> Thanks for the reply. >> Interesting, will "coercing" invoke an eval?
> No, it just changes a list into something that can be evaluated by > 'funcall'. > 'funcall' is where most of the heavy lifting (evaluation) is being > performed.
Oh, I see. So basically funcall will have to interpret the S-expression as a function (or invoke a compiler in the compiled code)? Does it mean that one should be careful with it if performance matters?
> > On Oct 25, 7:23 pm, Andy Venikov<swojchelo...@gmail.com> wrote: > >> On 10/25/2010 7:14 PM, vanekl wrote: > >> <snip>
> >>>> But isn't this code different from the original? > >>>> In your case function ff returns the result of evaluating S-expression > >>>> (lambda (x) (+ x n)) which will be a function-form. In the original > >>>> version, addn was returning the S-expression unevaluated. Someone has to > >>>> evaluate it to construct a function object, right?
> >>>> Thanks, > >>>> Andy.
> >>> Yes, you're right, the code is a bit different. Instead of evaluating > >>> a function, it's trying to evaluate a CONS (list).
> >>> Now it works, after coercing the list to a function.
> >> Thanks for the reply. > >> Interesting, will "coercing" invoke an eval?
> > No, it just changes a list into something that can be evaluated by > > 'funcall'. > > 'funcall' is where most of the heavy lifting (evaluation) is being > > performed.
> Oh, I see. So basically funcall will have to interpret the S-expression > as a function (or invoke a compiler in the compiled code)? > Does it mean that one should be careful with it if performance matters?
No, funcall is very fast. It's roughly as fast as making any other function call.
Andy Venikov <swojchelo...@gmail.com> writes: > On 10/25/2010 7:25 PM, vanekl wrote: >> On Oct 25, 7:23 pm, Andy Venikov<swojchelo...@gmail.com> wrote: >>> On 10/25/2010 7:14 PM, vanekl wrote: >>> <snip> >>>> Now it works, after coercing the list to a function.
>>> Thanks for the reply. >>> Interesting, will "coercing" invoke an eval?
>> No, it just changes a list into something that can be evaluated by >> 'funcall'. >> 'funcall' is where most of the heavy lifting (evaluation) is being >> performed.
> Oh, I see. So basically funcall will have to interpret the > S-expression as a function (or invoke a compiler in the compiled > code)? > Does it mean that one should be careful with it if performance matters?
FUNCALL calls the function, using APPLY.
APPLY may just jump to the function code if it's compiled to native code, or run byte-code virtual machine interpreter if it's compiled to bytecode, or run the interpreter if the function is to be interpreted.
COERCE may call COMPILE to generate a compiled version of the function, or may just implement the minimal-compilation specified, or may defer it to the interpreter.
So in terms of performance, when using COERCE, the repartition depends entirely on the implementation.
If you know that your implementation has a compiler (they almost all of them have one), then you could use COMPILE instead of COERCE, to ensure an early compilation.
And of course, if you don't call repeatitively your function, it might be better to interpreter it than to have the overhead of compiling it.
======================================================================== Clozure Common Lisp Version 1.5 (LinuxX8664)
Evaluation of (EVAL '(FUNCALL '(LAMBDA NIL (+ 1 2)))) produced nothing on *STANDARD-OUTPUT* produced nothing on *ERROR-OUTPUT* produced the following error: #<TYPE-ERROR #x3020005D3E9D> (LAMBDA NIL (+ 1 2)) is not of type (OR SYMBOL FUNCTION), and can't be FUNCALLed or APPLYed produced the following values: -->
Evaluation of (EVAL '(FUNCALL '(LAMBDA NIL (+ 1 2)))) produced nothing on *STANDARD-OUTPUT* produced nothing on *ERROR-OUTPUT* produced the following error: #<SIMPLE-TYPE-ERROR #x000333FA22D0>
FUNCALL: argument (LAMBDA NIL (+ 1 2)) is not a function. To get a function in the current environment, write (FUNCTION ...). To get a function in the global environment, write (COERCE '... 'FUNCTION).
Evaluation of (EVAL '(FUNCALL '(LAMBDA () (+ 1 2)))) produced nothing on *STANDARD-OUTPUT* produced nothing on *ERROR-OUTPUT* produced no error produced the following values: --> 3
; loading system definition from ; /usr/share/common-lisp/systems/asdf-binary-locations.asd into ; #<PACKAGE "ASDF0"> ; registering #<SYSTEM ASDF-BINARY-LOCATIONS {1002BFA1B1}> as ; ASDF-BINARY-LOCATIONS
Evaluation of (EVAL '(FUNCALL '(LAMBDA () (+ 1 2)))) produced nothing on *STANDARD-OUTPUT* produced nothing on *ERROR-OUTPUT* produced the following error: #<TYPE-ERROR {1002B6CE61}> The value (LAMBDA () (+ 1 2)) is not of type (OR FUNCTION SYMBOL). produced the following values: -->
> Thanks for the reply. > Interesting, will "coercing" invoke an eval?
sort-of. What it will do is look for either a symbol (in which case it will return the global function definition) or a list whose first element is LAMBDA (in which case it will return a lexical closure in the null environment). The second case can cause arbitrary evaluation to happen at the time COERCE is called, I am pretty sure (something like (coerce '(lambda () (load-time-value (progn (print "hi") 1)))) I think is an example of this).
However, what you are thinking may be that it just evaluates its argument to get a function, and that's not the case. It happens, now, to be the case that LAMBDA has a macro definition which means that (lambda ...) constructs a function, but COERCE doesn't care about that, and it worked before LAMBDA had this definition, which is a relatively late addition to CL.
Tim Bradshaw <t...@tfeb.org> wrote: +--------------- | Andy Venikov said: | > Interesting, will "coercing" invoke an eval? | | sort-of. What it will do is look for either a symbol (in which case it | will return the global function definition) or a list whose first | element is LAMBDA (in which case it will return a lexical closure in | the null environment). The second case can cause arbitrary evaluation | to happen at the time COERCE is called, I am pretty sure (something | like (coerce '(lambda () (load-time-value (progn (print "hi") 1)))) I | think is an example of this). +---------------
Minor nit to avoid confusing OP: the COERCE needs a 2nd arg of 'FUNCTION here:
#<Interpreted Function (LAMBDA () (LOAD-TIME-VALUE #)) {489A2509}> cmu> (funcall *)
"hi" 1 cmu> (funcall **)
1 cmu>
Note that the second FUNCALL didn't print "hi", since CMUCL [which I'm using above] actually does very little during the COERCE [it likewise does very little during the execution of an interpreted DEFUN], delaying "conversion" [minimal compilation] of the LAMBDA form to full internal interpreter form until the first time the function is called. It is during this "conversion" that the LOAD-TIME-VALUE was evaluated, hence the printing of "hi" during the first FUNCALL and not the second.
Note that the function is re-converted during full compilation to native code, hence the printing of "hi" during compilation and *not* during the first or second FUNCALL of the result.
You may very well get different results [by which I mean when "hi" gets printed or not] in other CL implementations.
-Rob
----- Rob Warnock <r...@rpw3.org> 627 26th Avenue <URL:http://rpw3.org/> San Mateo, CA 94403 (650)572-2607
??>> No, it just changes a list into something that can be evaluated by ??>> 'funcall'. ??>> 'funcall' is where most of the heavy lifting (evaluation) is being ??>> performed.
I don't think it is correct...
AV> Oh, I see. So basically funcall will have to interpret the S-expression AV> as a function (or invoke a compiler in the compiled code)?
No, actually funcall is a very simple operation like CPU's `call` instruction -- it just tells execution engine to start executing function at certain address (with certain arguments). The rest depends on execution engine.
I guess for implementation which work with native code funcall will be compiled to an instruction like CALL or JMP (with additional handling of parameters and return values, of course). And for bytecode interpreter it will do analogous thing...
While it is theoretically possible that implementation uses just-in-time compilation strategy, it doesn't really mean that funcall itself is expensive.
Conceptually, you can think that (coerce ... 'function), (compile ...) or (eval ...) do "the magic" to convert code to function. How it works in practice depends on implementation.
AV> Does it mean that one should be careful with it if performance AV> matters?
No, performance overhead is very low -- like an additional indirection overhead, if any...
If you care about performance maybe it is better to call (compile ...) instead of (coerce ... 'function). In some implementations they are equivalent, but in other ones compiled functions work a lot faster.
CO> I guess for implementation which work with native code funcall will be CO> compiled to an instruction like CALL or JMP (with additional handling CO> of parameters and return values, of course).
So if it knows that parameter x is a function it just does JMP.
If it does not know that it has to call SB-KERNEL:%COERCE-CALLABLE-TO-FUN first to get address of the function:
; in: LAMBDA NIL ; (FUNCALL X) ; --> SB-C::%FUNCALL THE ; ==> ; (SB-KERNEL:%COERCE-CALLABLE-TO-FUN FUNCTION) ; ; note: unable to ; optimize away possible call to FDEFINITION at runtime ; due to type uncertainty: ; The first argument is a (OR FUNCTION SYMBOL), not a FUNCTION.
p...@informatimago.com (Pascal J. Bourguignon) writes:
> In old lisps, and eg. in emacs lisp, it is possible to funcall a list > starting with lambda.
This was once true in Common Lisp: CLtL 2.13 says (in a section marked with change bars):
: A /function/ is anything that may be correctly given to the |funcall| : or |apply| function, and is to be executed as code when arguments are : supplied. ... A lambda-expression (a list whose car is the symbol : |lambda|) may serve as a function.