Kaz Kylheku
4/9/2015 2:57:00 PM
On 2015-04-09, zhz shi <messi.shizz@gmail.com> wrote:
> Hi all,
>
> I'm a newbie to common lisp and I know symbol is the key idea behind the
> whole language(and all LISP), but sometimes I found myself are really
> confused about it.
>
> Like this loop code snippet: (loop for i from 1 to 10 collect i), which will
> cause 5 symbols created and interned into current package(FOR, I, FROM, TO,
> COLLECT). (loop :for i :from 1 :to 10 :collect i) will intern related
> keywords in KEYWORD package.
Note that the ability to use symbols FOR, FROM, TO and COLLECT from any package
is a special feature of the LOOP macro only.
It allows you to use LOOP in any package without importing these clause symbols.
Without this feature, you would have to write:
(loop cl:for i cl:from ... cl:collect i)
assuming that CL:LOOP is visible in the current package, but CL:FOR and the
others are not.
There isn't anything wrong with making people import these, but somewhere
along the line, someone made the design decision to make the LOOP clause
identifiers behave as character strings rather than symbols.
Keywords could have been used. Then the syntax would have to be:
(loop :for i :from 1 :to 10 :collect i)
the designer apparently didn't like this.
> Could anybody kindly explain to me why CL is
> implemented this way? Is this a sort of pollution to packages(current package
> or keyword package) ?
Interning is essential because that's what identifies two occurrences of
a symbol in the same program as being the same object.
All programming languages perform interning in some form. C compilers do
interning. If you have:
{ int x = 3; ... foo(x); }
the x has to be entered into some symbol table in the compiler, so
the definition of x is properly connected to its later use.
That symbol table entry is likely retained throughout various compilation
passes, and optionally even into run-time (in the form of debugging info).
> Another is about the KEYWORD package, why is there a need of package KERWORD
> in cl?
The keyword package is useful because keywords have a special evaluation rule.
They evaluate to themselves.
If you invent a keyword symbol like :foo, you instantly get a token that you
can use throughout your program, such that all occurrences of that token refer
to the same symbol object. When that object is treated as a form to be evaluated,
it evaluates to that object itself: the same behavior as the forms 42 or "abc".
Other symbols, except for NIL and T (CL:NIL and CL:T) do not evaluate to themselves;
they are expected to denote variable bindings.
But why don't we just use the ' quote on regular symbols? If we want FOO to
evaluate to itself we can just use (QUOTE FOO) and write it as 'FOO which
is just as short as :FOO.
But with :FOO we have:
:foo -> :foo ;; foo evaluates to itself
(first '(:foo)) -> :foo ;; foo in a list is just itself
Whereas:
(defvar x 3)
x -> 3 ;; x evaluates to variable value via binding
(first '(x)) -> x ;; x in a list is itself
Self-evaluation is different from a quote operator!
> From HyperSpec it says "This makes it notationally convenient to use
> keywords when communicating between programs in different packages.", so what
> is the essential meaning of "communicating between programs in different
> packages"?
This is because :foo is a fully qualified symbol; it is a shorthand for KEYWORD:FOO.
So the meaning of :foo is the same no matter in what context it is read by
the Lisp reader, and it denotes the same thing everywhere.
If I write this function MYPACKAGE:
(defun fun (&key xyz))
you have to call that as (mypackage::fun ...) unless you make that more convenient
by bringing the symbol into the your package. But the keyword argument is just :xyz
just: (mypackage::fun :xyz 42).
Keywords are not just for communicating keyword arguments. Keywords give you a
supply of unique names for denoting properties, and members of sets and such.
C:
enum ice_cream_flavor { VANILLA, CHOCOLATE, ... } f = VANILLA;
switch (f) {
case VANILLA: ...
case CHOCOLATE: ...
...
}
Lisp:
(let ((f :vanilla))
...
(case f
(:vanilla ...)
(:chocolate ...)))
The difference is that keywords do not have numeric values (they are not an
enumeration); they are their own values, and we don't have a type "ice cream
flavor" which consists of a restricted subset of keywords.
Of course, we can define one.
;; the ice-cream-flavor type consists of three elements denoted by keywords:
(deftype ice-cream-flavor () '(member :vanilla :chocolate :strawberry))
;; check that the value of variable x is an ice-cream-flavor:
(check-type x ice-cream-flavor) ;; signals error
We could use non-keyword symbols. But the drawbacks are: knowing when to quote
and not to quote; and lack of visual distinction.
;; no quotes here
(deftype ice-cream-flavor () '(member vanilla chocolate strawberry))
(let ((x 'vanilla)) ;; quote here
;; ...
(case x
(vanilla ...) ;; no quote on CASE constants
((chocolate strawberry) ...)))
About the visual distinction, you know that if you see (:chocolate :strawberry)
then that is just keywords, and not a function call or any other syntax. Your
text editor can easily apply special syntax coloring to keywords, too.