Page 1 of 1

Help with functions that return functions

Posted: Thu Nov 12, 2009 7:43 pm
by oskarkv
Hello!

Another Lisp beginner here. I'm currently reading ANSI Common Lisp by Paul Graham (PG). I've come to the chapter on functions and the sub chapter on closures. There are some things that confuse me. *Thought about it for a while.* Hm, now that I have thought about it I think that I have pretty much worked out most of what confused me. But I'm probably wrong about some things so I'll just write some thoughts and you can point out to me where I'm wrong. There are some questions in there, too.

PG writes:

Code: Select all

(defun make-adder (n)
  #'(lambda (x)
      (+ x n)))
But apparently it works just as well without the #'. What's the difference? Why do PG write #' if it isn't needed? I thought #' meant something like "Get the function that this symbol refers to, from the function namespace (or whatever it's called)." But (lambda ...) isn't a symbol, so I shouldn't need it, right? But then, why does it even work to prefix (lambda ...) with #'? I mean, if it doesn't make sense I should get an error.

Another thought I had was that it would be neat if I could do ((make-adder 3) 2) and get 5 (almost wrote 6, that would be neat). But apparently I can't. I don't understand exactly why, although I know that forms (I'm not sure I even know what a "form" is exactly) are evaluated only (?) when they are arguments to functions. So, maybe (make-adder 3) is not evaluated when I write ((make-adder 3) 2) because it's not an argument to a function. Then I tried (setf a (make-adder 3)) and (a 3) but that didn't work. I guess that's because a is now a variable in the namespace of, hm, regular values, and not in the function namespace (What are they usually called?), right? (setf (symbol-function 'a) (make-adder 3)) and then (a 3) worked. Of course, I know about (funcall (make-adder 3) 3) so I don't need to use symbol-function all the time.

So I guess there were only two proper questions in there: Why PG writes #'(lambda ...) and why ((make-adder 3) 6) doesn't work. Although, if you notice that I've got something wrong, or know of any pits I might fall into, please point that out.

Re: Help with functions that return functions

Posted: Mon Nov 16, 2009 11:45 am
by Kompottkin
oskarkv wrote:But apparently it works just as well without the #'. What's the difference?
#'foo is a short-hand notation for (function foo). #'(lambda (x) (+ x n)) is therefore the same as (function (lambda (x) (+ x n))).

Once upon a time, the special operator that did all the magic in Lisp lambda instantiation was FUNCTION. Nowadays, it's not as important to know what the compiler does internally, but the FUNCTION operator is conceptually still the primitive while LAMBDA is a mere macro.

#'(lambda (x) (+ x n)) has the same effect when evaluated as (lambda (x) (+ x n)), because the latter macroexpands into the former.
Another thought I had was that it would be neat if I could do ((make-adder 3) 2) and get 5 (almost wrote 6, that would be neat). But apparently I can't. I don't understand exactly why, although I know that forms (I'm not sure I even know what a "form" is exactly) are evaluated only (?) when they are arguments to functions ...
Yes, but there's more to it than that. Consider the following:

Code: Select all

CL-USER> (defun head (list)
           (car list))
HEAD
CL-USER> (defun (setf head) (new-value list)
           (setf (car list) new-value))
(SETF HEAD)
CL-USER> (defvar *x* (cons 1 2))
*X*
CL-USER> (setf (head *x*) 100)
100
CL-USER> *x*
(100 . 2)
What happens here is that we define an accessor by defining a reader called head along with a setf method called (setf head) (that is the actual function name!), which setf will use whenever we do a (setf (head ...) ...) call.

Now, given that, what do you expect the following to do?

Code: Select all

CL-USER> ((setf head) *x* 100)
Will it call the function called (setf head)? Or will it try to evaluate the expression (setf head) and use its return value as the function to call, as you're suggesting?

I'd say either interpretation would make sense. And in fact, the spec doesn't favour one interpretation over the other. What it says is that the expression is invalid.

Re: Help with functions that return functions

Posted: Mon Nov 16, 2009 11:59 am
by ramarren
For added confusion try this ;)

Code: Select all

(setf (symbol-function 'a) (lambda () "global-a"))
(setf (symbol-value 'a) (lambda () "variable-global-a"))
(let ((a (lambda () "variable-local-a")))
  (flet ((a () "local-a"))
    (print (a))
    (print (funcall #'a))
    (print (funcall 'a))
    (print (funcall (symbol-function 'a)))
    (print (funcall a))
    (print (funcall (symbol-value 'a)))
    (values)))

Re: Help with functions that return functions

Posted: Mon Nov 16, 2009 1:31 pm
by oskarkv
Kompottkin wrote:

Code: Select all

CL-USER> ((setf head) *x* 100)
Will it call the function called (setf head)? Or will it try to evaluate the expression (setf head) and use its return value as the function to call, as you're suggesting?

I'd say either interpretation would make sense. And in fact, the spec doesn't favour one interpretation over the other. What it says is that the expression is invalid.
Well, shouldn't one call it like this instead: ((setf head) 100 *x*) because the first argument is suppposed to be the new-value and the second argument is the list? And in fact this works for me (I'm using CLisp).
Ramarren wrote:For added confusion try this ;)

Code: Select all

(setf (symbol-function 'a) (lambda () "global-a"))
(setf (symbol-value 'a) (lambda () "variable-global-a"))
(let ((a (lambda () "variable-local-a")))
  (flet ((a () "local-a"))
    (print (a))
    (print (funcall #'a))
    (print (funcall 'a))
    (print (funcall (symbol-function 'a)))
    (print (funcall a))
    (print (funcall (symbol-value 'a)))
    (values)))
That was very illuminating.

Well, I think I have only one small question for now: When I looked up the special operator function I found that it's supposed to take a function name or lambda expression. In the case (funcall #'a), a is not a lambda expression, so it must be a function name. But, I thought that just writing a (as in (function a)) refers to the value of the symbol a. And that's supposed to be a so called function name? I'm not sure I've got this right. Hm, now that I think about it, maybe just writing a does only refer the value of the symbol a when it is a function argument, but since function is a special operator, a in (function a) refers to something else.

Well, thank you both of you. Your posts certainly made these things clearer for me.

Re: Help with functions that return functions

Posted: Mon Nov 16, 2009 1:51 pm
by ramarren
oskarkv wrote:And that's supposed to be a so called function name?
You can check the exact meaning of the term function name in Hyperspec glossary.
oskarkv wrote:Hm, now that I think about it, maybe just writing a does only refer the value of the symbol a when it is a function argument, but since function is a special operator, a in (function a) refers to something else.
Special operators indeed have custom evaluation rules, that is what makes them different from functions. In this case the entire point of FUNCTION operator is to retrieve the function named by its argument from current environment.

Macros also can have custom evaluation, they are different from special operators in that they have to expand to some combination of functions and special operators. Knowing which parts of the source are evaluated in what context (if at all) is a necessary skill when programming Lisp. Unfortunately, some people seem to be unable to get used to it without syntax hints, which might be where some of the animosity towards Lisp comes from.

Re: Help with functions that return functions

Posted: Mon Nov 16, 2009 2:38 pm
by oskarkv
Thanks again!

Is is correct to say that function does the same thing that symbol-function does only in the current environment instead of the global environment?

Re: Help with functions that return functions

Posted: Mon Nov 16, 2009 4:03 pm
by ramarren
oskarkv wrote:Thanks again!

Is is correct to say that function does the same thing that symbol-function does only in the current environment instead of the global environment?
Not really. Well, maybe a bit...

SYMBOL-FUNCTION is a function, and an accessor. One thing which might not be immediately obvious is that symbols in Common Lisp are first class objects similar to structures. When implemented they are usually not literally structures for optimization reasons (they are usually sparse for one thing), but they do have slots, at least name, value, function-value, package and plist. SYMBOL-FUNCTION just accesses one of this slots. The implementation is, I think, required to prevent non-function object from being stored in function-value slot for reasons of basic sanity, but it is not the function which is retrieved from global environment, the symbol is. And it doesn't even really have to be, either, see:

Code: Select all

(defun a ()
  (print "global-a"))
(let ((a (make-symbol "not global symbol")))
  (setf (symbol-function a) (lambda ()
                              (print "function in local symbol slot")))
  (funcall (symbol-function a))
  (funcall (symbol-function 'a))
  (values))
Not how in one expression time 'a' is not quoted, which means the value of the variable lexically named by 'a', which happens to be a different symbol, is accessed.

On the other hand FUNCTION is a special operator which first looks for a function of a given name in lexical environment, and then the global one (that is, it goes through symbol-function of the symbol given to it). Since lexical environments are not first-class in standard Common Lisp, it has to be specially treated by the compiler.

Re: Help with functions that return functions

Posted: Sun Nov 22, 2009 1:46 am
by Kompottkin
oskarkv wrote:
Kompottkin wrote:

Code: Select all

CL-USER> ((setf head) *x* 100)
...
Well, shouldn't one call it like this instead: ((setf head) 100 *x*) because the first argument is suppposed to be the new-value and the second argument is the list?
Yup, you're right. :)