What is the difference between the keyword symbol
:foo
and the quoted symbol:
'foo
Both stand for themselves, and can be used as an identifier. I can see that keyword symbols are mainly used for named parameters, but I was asking myself if it was not possible to implement this using quoted symbols as well?
In other words: Why do I need both?
First: 'something
is a shorter notation for (quote something)
. The reader will transform the quote character into a list with the symbol cl:quote
as the first item. For the evaluator it means: don't evaluate something
, just return it as a result.
CL-USER 22 > '(quote foo) (QUOTE FOO) CL-USER 23 > ''foo (QUOTE FOO) CL-USER 24 > (read-from-string "'foo") (QUOTE FOO)
The colon :
is a package marker. If the package name is missing, the symbol is in the KEYWORD
package.
We can give foo
a value:
CL-USER 11 > (setq foo 10) 10
foo
evaluates to its value.
CL-USER 12 > foo 10
A quoted symbol evaluates to the symbol.
CL-USER 13 > 'foo FOO
We can't give :foo
a value:
CL-USER 14 > (setq :foo 10) Error: Cannot setq :FOO -- it is a keyword. 1 (abort) Return to level 0. 2 Return to top loop level 0. Type :b for backtrace or :c <option number> to proceed. Type :bug-form "<subject>" for a bug report template or :? for other options. CL-USER 15 : 1 > :top
:foo
already has a value: itself.
CL-USER 16 > :foo :FOO
Naturally a quoted :foo
evaluates to :foo
.
CL-USER 17 > ':foo :FOO
The symbol foo
is in some package, here CL-USER
.
CL-USER 18 > (symbol-package 'foo) #<The COMMON-LISP-USER package, 92/256 internal, 0/4 external>
The symbol :foo
is in the KEYWORD
package.
CL-USER 19 > (symbol-package ':foo) #<The KEYWORD package, 0/4 internal, 6230/8192 external>
Since :foo
is the value of :foo
we can also write:
CL-USER 20 > (symbol-package :foo) #<The KEYWORD package, 0/4 internal, 6230/8192 external>
:foo
is an abbreviation for keyword:foo
. Thus the symbol is in the keyword package and it is exported.
CL-USER 21 > keyword:foo :FOO
So keyword symbols are self-evaluation constant symbols in the keyword package. They are used as markers in data structures and in keyword arglists. The good things: you don't need to struggle with packages and they evaluate to themselves - so a quote is not needed.
Rainer Joswig's answer describes the symbols themselves pretty well. To summarize, though, each symbol belongs to a package. p::foo
(or p:foo
, if it's external) is a symbol in the package p
. If you try to evaluate it as form, you'll get its symbol-value, which you can set with set
, or `(setf symbol-value):
CL-USER> (set 'foo 'bar) BAR CL-USER> foo BAR CL-USER> (setf (symbol-value 'foo) 'baz) BAZ CL-USER> foo BAZ
There's a special package named keyword
. You can write keyword::foo
if you want, but all of the keyword package's symbol are external, so you can write keyword:foo
instead. Because they're so commonly used, though, you even get a special syntax for them: :foo
. They've also got the special property that you can't set their value; their values are themselves:
CL-USER> :foo :FOO CL-USER> (symbol-value :bar) :BAR
And that's really all there is that makes keyword symbols special, in and of themselves.
What's probably a bit more important is that they are, by default, used as indicators for "keyword arguments" in lambda lists. E.g.,
CL-USER> ((lambda (&key foo bar) (list foo bar)) :bar 23 :foo 12) (12 23)
I can see that keyword symbols are mainly used for named parameters, but I was asking myself if it was not possible to implement this using quoted symbols as well?
The syntax for lambda lists actually lets you do a lot more customization with the keyword arguments. A common thing is to specify default values:
CL-USER> ((lambda (&key (foo 'default-foo) bar) (list foo bar)) :bar 23) (DEFAULT-FOO 23)
You can also provide a variable name that gets bound to a boolean indicating whether the parameter was specified or not:
CL-USER> ((lambda (&key (foo 'default-foo foo-p) (bar 'default-bar bar-p)) (format t "~{~A:~7t~A~%~}" (list 'foo foo 'foo-p foo-p 'bar bar 'bar-p bar-p))) :bar 23) FOO: DEFAULT-FOO FOO-P: NIL BAR: 23 BAR-P: T
The full syntax for from 3.4.1 Ordinary Lambda Lists lets us do even more, though. It's
lambda-list::= (var* [&optional {var | (var [init-form [supplied-p-parameter]])}*] [&rest var] [&key {var | ({var | (keyword-name var)} [init-form [supplied-p-parameter]])}* [&allow-other-keys]] [&aux {var | (var [init-form])}*])
Note that you can specify the keyword-name
. It defaults to the symbol in the keyword package with the same name as var
, but you can actually provide it and specify your own "keywords". This can be handy, e.g., if you want a descriptive keyword name but don't want such a long variable name:
CL-USER> ((lambda (&key ((:home-directory dir))) (list dir)) :home-directory "/home/me") ("/home/me")
You can also use it to specify keyword names that aren't keyword symbols:
CL-USER> ((lambda (&key ((surprise surprise))) (list surprise)) 'surprise "hello world!") ("hello world!")
You can combine the two, too:
CL-USER> ((lambda (&key ((hidden-parameter secret))) (format t "the secret is ~A" secret)) 'hidden-parameter 42) the secret is 42
You can mix that with default values as well, but you probably get the point by now.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With