简体   繁体   中英

Common Lisp - CCL, why a warning when passing a global to a local function?

I'm learning Common Lisp, using CCL. I get a warning when I use a global variable locally. Why does CCL provide this functionality? What is the purpose of this?

(setf n 75)

;;;This function works, but gets a warning about
;;;n being an undeclared free variable.
(defun use-global ()

(write n)
)

;;;This function works without warnings.
(defun global-to-local (x)

(write x)
)

(use-global)
(global-to-local n)

Setf and setq do not introduce new variables, they modify existing ones.

In order to define something like global variables, use defvar or defparameter . These are conventionally written with a leading and ending * , and they are automatically declared globally special . This means that whenever you re-bind them, this binding is in effect for the entire call stack above it, whatever functions you call from there etc..

In your example, the toplevel setf did not do that, so when compiling the function use-global , the compiler does not see that n is meant as such and issues the warning. In correct and idiomatic Common Lisp code, this warning should generally be treated as an error, since it then indicates a misspelling or typo.

The warning provides little value. The variable is bound by a prior top-level assignment, which creates a binding in the global environment making that variable a global variable. It simply being accessed, which is likely what is intended by the programmer.

An unbound variable warning is very valuable when no definition of a variable is seen because it catches misspellings of variable references. But a top-level setf or setq should be noticed by the implementation and treated as a definition.

It is useful to have a warning for cases when a variable is defined by a top-level setf and then later subject to a let :

(setf n 42)

(let ((n 43)) (func))

Here, it looks like the programmer might be expecting n to be a special variable, but it wasn't defined that way. func will see the top level binding of n which holds 42, and not the lexical n which holds 43. So there is a good reason to have a diagnostic here. The intent of the code requires n to have been declared via defvar or defparameter , or proclaimed special.

(Of course, we cannot warn about (let ((n 43)) ...) when no top-level n has been seen, because this is the usual situation for the vast majority of lexical variables.)

There is a bit of a defect in ANSI Common Lisp in that the section 3.1.2.1.1 Symbols as Forms says that there are only three kinds of variables: dynamic, lexical and constant. A dynamic variable is one that is declared special, and so according to this reasoning, setf is not enough to create a dynamic variable. However, section 3.1.1.1 clearly spells out that there exists a global environment. Confusingly, it says that it contains bindings that have indefinite extent and scope, and that it contains dynamic variables. But then "dynamic variable" is defined in the glossary as being a variable with a binding in the dynamic environment, and a dynamic environment is defined as containing bindings with "dynamic extent". So that can't be the global environment. You know, that one which 3.1.1.1 says contains dynamic variables!

Anyway, all this ANSI confusion has created the misconception that setf or setq cannot create a variable, which lends support to bogus compiler diagnostics.

Quick googling suggests in Common Lisp is it done as something like:

(defvar *n* 75)

(defun use-global () (write *n*))

(use-global)

Note the asterisk that decorate global names by convention.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM