A symbol which is in non-function position is treated as the name of a variable. In (function variable)
function
is in function-position (after the opening parenthesis) and variable
is not. Unless explicitly quoted variables are replaced with their values.
If you were to write (boundp my-variable)
that would mean "is the symbol which is stored in the value of variable my-variable
bound as a variable" and not "is the symbol my-variable
bound as a variable.
So why does bound-and-true-p
behave differently?
This is a macro and the normal (function) evaluation rules don't apply here, macros are free to decide if and when their arguments are evaluated. What macros actually do is somehow transform the arguments and return the result as a list, which is then evaluated. The transformation and the final evaluation happen at different times, called macro-expansion-time and evaluation-time.
This is what the definition of bound-and-true-p
looks like:
(defmacro bound-and-true-p (var)
"Return the value of symbol VAR if it is bound, else nil."
`(and (boundp (quote ,var)) ,var))
This uses reader macros which are different from lisp macros (more on that below). To not complicate this further lets not use any reader macros:
(defmacro bound-and-true-p (var)
"Return the value of symbol VAR if it is bound, else nil."
(list 'and (list 'boundp (list 'quote var)) var))
If you write
(bound-and-true-p my-variable)
that is first "translated" to
(and (boundp 'my-variable) my-variable)
and then that is evaluated returning nil
if my-variable
is not boundp
or else the value of my-variable
(which of course can also be nil
).
You might have noticed that the expansion wasn't
(and (boundp (quote my-variable)) my-variable)
as we could have expected. quote
is a special form, not a macro or function. Like macros, special forms can do whatever with their arguments. This particular special form simply returns its argument, here a symbol, instead of the variable value of the symbol. That's actually the only purpose of this special form: preventing evaluation! Macros cannot do that on their own, they need to use quote
to do so.
So what's up with '
? It is a reader macro, which as mentioned above isn't the same as a lisp macro. While macros are used to transform code/data, reader macros are used earlier when reading text in order to transform that text into code/data.
'something
is a short form for
(quote something)
`
used in the actual definition of bound-and-true-p
also is a reader macro. If it quotes a symbol as in `symbol
it is equivalent to 'symbol
, but when is used to quote a list as in `(foo bar ,baz)
it behaves differently in that forms that are prefixed with ,
are evaluated.
`(constant ,variable)
is equivalent to
(list (quote constant) variable))
This should answer the question why unquoted symbols are sometimes evaluated (replaced with their values) and sometimes not; macros can use quote
to prevent a symbol from being evaluated.
But why is bound-and-true-p
a macro while boundp
is not? We have to be able to determine if arbitrary symbols, which are not known until run-time, are bound as symbols. This would not be possible if boundp
argument was automatically quoted.
bound-and-true-p
is used to determine whether a known variable is defined and if so use its value. This is useful if a library has an optional dependency on a third-party library as in:
(defun foo-get-value ()
(or (bound-and-true-p bar-value)
;; we have to calculate the value ourselves
(our own inefficient or otherwise undesirable variant)))
bound-and-true-p
could be defined as a function and require the argument to be quoted but because it is intended for cases where you know upfront what variable you care about a macro was used to save you from having to type the '
.
setq
stands forset quoted
, and originally was a macro that expanded into(set 'some-variable "less")
. In general, Elisp isn't terribly consistent about quoted vs. unquoted arguments, but any function (not macro) that needs to interact with a variable instead of a value will take its argument quoted (setq
being a major exception).bound-and-true-p
is a stupid macro. Or rather, its name is stupid. 99.99% of the time when you want to do(and (boundp 'FOO) FOO)
you do it to use the value ofFOO
. You don't do it just to get a truth value. (1) The macro is not needed - the code it replaces is trivial and small. (2) The name is misleading - it is about the variable value, not just testing whether or not the variable value isnil
.