Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between a keyword symbol and a quoted symbol?

Tags:

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?

like image 967
Golo Roden Avatar asked May 31 '14 10:05

Golo Roden


2 Answers

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.

like image 184
Rainer Joswig Avatar answered Sep 19 '22 13:09

Rainer Joswig


DIfferences between keywords and other symbols

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.

Keywords and other symbols as keyword names in lambda lists

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.

like image 35
Joshua Taylor Avatar answered Sep 22 '22 13:09

Joshua Taylor