What is the difference between a keyword symbol and a quoted symbol?
Asked Answered
H

2

25

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?

Hardnosed answered 31/5, 2014 at 10:54 Comment(3)
You don't have to use keyword symbols as "keyword arguments"; it's just the default. I've added an answer that shows how you can use, e.g., quoted symbols as keyword names in lambda lists.Blight
@JoshuaTaylor: though quoted symbols as keywords in argument lists seems to be used very rarely in the code bases I've seen.Conqueror
@RainerJoswig I agree entirely. I don't think I've ever see it in practice, to be honest, except maybe in emulating Scheme. The only use case that I've thought of, really, would be using non-exported symbols to indicate that something isn't a supported keyword option, but it's still there. E.g., maybe something like (frob bar baz :from-end t 'frobber::include-debug-output t).Blight
C
23

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.

Conqueror answered 31/5, 2014 at 11:7 Comment(4)
Hmmm, but that's exactly what I don't get: If 'foo evaluates to foo, and :foo evaluates to :foo, isn't it effectively the same? As you said, you can not assign a value to :foo, but neither can I assign a value to 'foo, can I?Hardnosed
@GoloRoden: 'foo is a shorthand notation for (quote foo). Giving the list a value makes no sense. Since :foo evaluates to :foo it is not necessary to quote it.Conqueror
I think it's important to note that :foo evaluates to :foo, so it is self-evaluating, while 'foo evaluates to foo and as such is not. So it is not the same.Felton
Also worth noting, might be: (string= (symbol-name 'foo) (symbol-name :foo)). I.e. The symbol name of a keyword does NOT contain the colon.Amieeamiel
B
11

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.

Blight answered 31/5, 2014 at 12:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.