How to store a function in a variable in Lisp and

2019-07-17 23:53发布

问题:

I want to store a function like print in a variable so that I can just type something short like p, e.g:
In Scheme:

(define print display)
(print "Hello world\n")
;; alternate way
(define print 'display)
((eval print) "Hello world\n")

The same approach does not seem to work in Common Lisp:

(defvar p 'print)
;;(print (type-of p))
(p "Hello world") ;; Attempt 1
((eval p) "Hello world") ;; >> Attempt 2
((eval (environment) p) "Hello world") ;; Attempt 3

I'm getting this error with Attempt 1 above:

*** - EVAL: undefined function P

And this with Attempt 2 and 3 in Clisp:

*** - EVAL: (EVAL (ENVIRONMENT) P) is not a function name; try using a 
            symbol instead
*** - EVAL: (EVAL P) is not a function name; try using a symbol instead

And with gcl:

Error: (EVAL P) is invalid as a function.
Error: (EVAL (ENVIRONMENT) P) is invalid as a function.

So:

  • What does try using a symbol mean? p is definitely a symbol; false positive?
  • What's up with eval? Doesn't the evaluation of p yield the procedure print?
  • I thought Lisp procedures were first class objects. Why is Attempt 1 not working like in Scheme?

EDIT
(Moved from a comment below)

I was wondering why (setf (symbol-function 'p) #'print) won't work this way
(setf (symbol-function 'p) 'print). I get the following(not so helpful) error:

*** - SYSTEM::%PUTD: PRINT is not a function ;; CLisp
Error: PRINT is not of type LIST. ;; Gcl

I know that the sharp sign(#) is supposed to disambiguate between a function and a variable
with the same name but in this case, there's only one print, the function.

Also, why won't it work with defvar instead of setf like so:

(defvar (symbol-function 'p) #'print)

yet defvar and setf both assign values to a variable.
The associated error is:

*** - DEFVAR: non-symbol (SYMBOL-FUNCTION 'P) cannot be a variable ;; Clisp
Error: (SYMBOL-FUNCTION (QUOTE P)) is not of type SYMBOL. ;; Gcl

回答1:

Common Lisp is a "Lisp-2". Among other things, the first position in a function call is evaluated in the "function namespace". In your case, the symbol p names a variable, not a function.

This works better:

(defvar p 'print)

(funcall p "Hello world")

Or possibly, but you probably don't want to do this:

(setf (symbol-function 'p) #'print)

(p "Hello world")


回答2:

Common Lisp has a separate namespace for functions, which makes operation like this more verbose than with Scheme. If you would like similar to top level (define p display) in CL you should make a macro:

(defmacro defun-alias (name original-name)
   `(setf (symbol-function ',name) #',original-name))

Which works as this:

(defun-alias pc princ)
(pc "Hello") ; prints hello

Unlike Scheme define this will only overwrite a global binding. Thus:

(flet ((test (x) (+ x x))) 
   (defun-alias test +)
   (test 10))

will set global definition of #'test to #'+ and return 20. eg. it works like defun.



回答3:

To complement the other good answers with direct answers to your questions:

What does try using a symbol mean? p is definitely a symbol; false positive?

Read the error messages literally: (EVAL (ENVIRONMENT) P) and (EVAL P) (unevaluated) are not symbols, but lists. In Common Lisp, the car of a form is not evaluated.

What's up with eval? Doesn't the evaluation of p yield the procedure print?

eval is never called by your code (see previous answer). Even if it were, the result would be the symbol-value of the symbol print, not the symbol-function/fdefinition.

I thought Lisp procedures were first class objects. Why is Attempt 1 not working like in Scheme?

This has nothing to do with functions (I think the Common Lisp standard does not use the term "procedures" as the Scheme standards do.) being first class objects. This works in Common Lisp:

(let ((p #'print))
  (funcall p "hello world"))

Edit:

Answers to the extra questions:

I was wondering why (setf (symbol-function 'p) #'print) won't work this way (setf (symbol-function 'p) 'print).

It's not really true that "the sharp sign(#) is supposed to disambiguate between a function and a variable with the same name", as you write later. 'print expands to (quote print), so it evaluates to the symbol print instead of its value as a variable. #'print expands to (function print), so it evaluates to the value of the function cell of the symbol print instead. Whether print currently has a value as a variable is completely irrelevant to what #'print evaluates to.

Setting (symbol-function 'p) to the symbol print obviously won't make p call the function print because the symbol print is not the same as the function bound to the symbol print.

Also, why won't it work with defvar instead of setf like so:

(defvar (symbol-function 'p) #'print)

yet defvar and setf both assign values to a variable.

setf assigns values to places. The term (symbol-function 'p) denotes the place that is the function cell of the symbol p.

defvar defines new global variables. Its first argument needs to be a symbol that names the variable and can not be any kind of place.