Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

keywords vs. quoted symbols

144 views
Skip to first unread message

David Bakhash

unread,
May 31, 1998, 3:00:00 AM5/31/98
to

Hey,

Because my Lisp background is in elisp, I sometimes find myself in a
situation where my coding style can be improved. Here's a case with
symbols and keywords.

Say there exists a function in a package called `my-package' which
examines the global variable *data* and decices what type of data it
is. Imagine that after examination, it can decide that the data is
`continuous' or `discrete'.

Question:

(defun guess-type ()
(if (some-pred *data*)
:continuous ;; ### 'continuous ###
:discrete ;; ### 'discrete ###
))

is it better to use the keywords `:continuous' and `:discrete', or is
it better to pass regular symbols 'continuous and 'discrete ???

I am very confused about keywords in general. here's something that
seems odd:

(defclass tree-node ()
((data
:documentation "Virtual data"
:accessor data ;; ### symbol ###
:initarg :data ;; ### keyword ###
:type list)))

Shouldn't it be understood that the initarg is always gonna be a
keyword? when you specifiy keyword arguments in a function's
parameter list, you don't put the semicolens:

(defun f (x &key y z)
(...))

and NOT:

(defun f (x &key :y :z)
(...))

so what's the deal?

David J. Cooper Jr

unread,
May 31, 1998, 3:00:00 AM5/31/98
to

I'll make a stab at this, but in some cases I'm working from intuition
rather than being able to quote any biblical references:

David Bakhash wrote:

> Hey,
>
> Because my Lisp background is in elisp, I sometimes find myself in a
> situation where my coding style can be improved. Here's a case with
> symbols and keywords.
>
> Say there exists a function in a package called `my-package' which
> examines the global variable *data* and decices what type of data it
> is. Imagine that after examination, it can decide that the data is
> `continuous' or `discrete'.
>
> Question:
>
> (defun guess-type ()
> (if (some-pred *data*)
> :continuous ;; ### 'continuous ###
> :discrete ;; ### 'discrete ###
> ))
>
> is it better to use the keywords `:continuous' and `:discrete', or is
> it better to pass regular symbols 'continuous and 'discrete ???

I believe it is better for this function to return the keywords, since
they are immune from package. If you were to use regular symbols here,
they would wind up being interned in the ``my-package'' package, which
would complicate any comparisons being done back in the calling
function.

>
>
> I am very confused about keywords in general. here's something that
> seems odd:
>
> (defclass tree-node ()
> ((data
> :documentation "Virtual data"
> :accessor data ;; ### symbol ###
> :initarg :data ;; ### keyword ###
> :type list)))
>
> Shouldn't it be understood that the initarg is always gonna be a
> keyword? when you specifiy keyword arguments in a function's
> parameter list, you don't put the semicolens:
>
> (defun f (x &key y z)
> (...))
>
> and NOT:
>
> (defun f (x &key :y :z)
> (...))
>
> so what's the deal?

Within the body of the function f, you want to be able to refer to y
and z as if they are normal variables -- and you can't do that with
keywords. Therefore no colons are used in the argument list (so that
the names in the argument list match the names inside the function
body).

--
David J. Cooper Jr. Genworks International
dcoo...@genworks.com http://www.genworks.com

"...Embracing an Open Systems approach to Knowledge-based Engineering..."


Scott L. Burson

unread,
May 31, 1998, 3:00:00 AM5/31/98
to

David Bakhash wrote:
>
> is it better to use the keywords `:continuous' and `:discrete', or is
> it better to pass regular symbols 'continuous and 'discrete ???

Generally when using constant symbols as data I find it's better to use
keyword symbols, particularly when they are being returned as values
from some public interface that may be used by code in other packages.
Otherwise the client code has to use explicit package prefixes, which
then means it can't be compiled without this package being loaded.

The most common exception is when the symbol is being used strictly as
an internal value and I specifically *don't* want clients to mention it.

A minor stylistic point: when I am writing a keyword symbol as a datum
rather than as syntax (to introduce a keyword argument, as an option to
DEFCLASS, etc. etc.), I like to quote it like a normal symbol, even
though this is technically not necessary. Thus:

(defun foo (x y)
(if (member x y :test #'some-test)
':yes
':no))

I don't know anyone else who does this, but I think it enhances
readability, particularly if you are using XEmacs fontification. Of
course, I see I neglected to do it in my previous post :-(

> I am very confused about keywords in general.

I would guess that almost anything having to do with packages is
confusing on first sight.

> here's something that
> seems odd:
>
> (defclass tree-node ()
> ((data
> :documentation "Virtual data"
> :accessor data ;; ### symbol ###
> :initarg :data ;; ### keyword ###
> :type list)))
>
> Shouldn't it be understood that the initarg is always gonna be a
> keyword?

Well, I'd put it differently. The DEFCLASS option should have been
named :INITKEYWORD rather than :INITARG.

-- Scott

* * * * *

To use the email address, remove all occurrences of the letter "q".

David J. Cooper Jr

unread,
May 31, 1998, 3:00:00 AM5/31/98
to

David J. Cooper Jr wrote:

>
>
> David Bakhash wrote:
>
>
> >
> > I am very confused about keywords in general. here's something that


> > seems odd:
> >
> > (defclass tree-node ()
> > ((data
> > :documentation "Virtual data"
> > :accessor data ;; ### symbol ###
> > :initarg :data ;; ### keyword ###
> > :type list)))
> >
> > Shouldn't it be understood that the initarg is always gonna be a

> > keyword? when you specifiy keyword arguments in a function's
> > parameter list, you don't put the semicolens:
> >
> > (defun f (x &key y z)
> > (...))
> >
> > and NOT:
> >
> > (defun f (x &key :y :z)
> > (...))
> >
> > so what's the deal?
>
> Within the body of the function f, you want to be able to refer to y
> and z as if they are normal variables -- and you can't do that with
> keywords. Therefore no colons are used in the argument list (so that
> the names in the argument list match the names inside the function
> body).

I see your actual question here is about :initarg in defclass, which I didn't
answer. Sorry.

Anyone up for tackling David's actual question? I'm not much of a CLOS guy yet
(still trying to master ``defpart'' in ICAD)...

Barry Margolin

unread,
Jun 1, 1998, 3:00:00 AM6/1/98
to

In article <cxj7m32...@hawk.bu.edu>, David Bakhash <ca...@bu.edu> wrote:
>I am very confused about keywords in general. here's something that
>seems odd:
>
>(defclass tree-node ()
> ((data
> :documentation "Virtual data"
> :accessor data ;; ### symbol ###
> :initarg :data ;; ### keyword ###
> :type list)))
>
>Shouldn't it be understood that the initarg is always gonna be a
>keyword? when you specifiy keyword arguments in a function's
>parameter list, you don't put the semicolens:

The initarg doesn't have to be a keyword, that's just a common style. In
fact, it would generally be best if classes that are intended to be
subclassed by users in other packages *don't* use keywords as their
initargs. This way, if the user names one of his slots similarly to one of
the parent's slots (differing only in the package) there won't be a
conflict between the initargs.

>(defun f (x &key y z)
> (...))
>
>and NOT:
>
>(defun f (x &key :y :z)
> (...))
>
>so what's the deal?

&key y should be considered a shorthand for &key ((:y y)). It's doing two
things: naming the local variable that holds the parameter, and naming the
keyword used by the caller to pass it. Normally, the latter is the keyword
form of the former, but the expanded syntax allows you to override this.

In DEFCLASS, the slot name is analogous to the former, the :INITARG option
is analogous to the latter, but there's no automatic defaulting. Since the
:INITARG option isn't defining a variable, there's no reason for it to
contain a non-keyword when the initarg is actually a keyword.

--
Barry Margolin, bar...@bbnplanet.com
GTE Internetworking, Powered by BBN, Cambridge, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.

Kent M Pitman

unread,
Jun 1, 1998, 3:00:00 AM6/1/98
to

"Scott L. Burson" <Gy...@zeta-sqoft.com> writes:

> Generally when using constant symbols as data I find it's better to use

> keyword symbols, [...]

I have a more elaborate personal style rule. It says to use keywords
when dealing with a finite, non-extensible data space; otherwise,
packaged symbols. e.g.,...

- GET should not use keywords because there are an unbounded number
of possible keys.

- MAKE-INSTANCE (oops--"water under the bridge", as they say)
I believe should never have used keywords.
The reason is because it's extensible, and conflicting classes
might choose the same keyword; better to let the package space
keep them from getting in trouble. (I usually use keywords
with MAKE-INSTANCE because that's what most people expect, but
I tend to think it was a wrong choice.

- A function that returns one of a fixed, finite set of states
like FIND-SYMBOL's second value does right by returning a keyword.

I'm not rigid about the rule, but I find it's often left me with a good
set of intuitions...

David Bakhash

unread,
Jun 1, 1998, 3:00:00 AM6/1/98
to

Kent M Pitman <pit...@world.std.com> writes:

> "Scott L. Burson" <Gy...@zeta-sqoft.com> writes:
> I have a more elaborate personal style rule. It says to use keywords
> when dealing with a finite, non-extensible data space; otherwise,
> packaged symbols. e.g.,...
>
> - GET should not use keywords because there are an unbounded number
> of possible keys.

...should not use keywords for keys or for values? This is ambiguous
to me.

(defvar x nil)

i.e. are you suggesting against doing:

(setf (get 'x 'some-prop) :big) (1)

or are you saying not to do this:

(setf (get 'x :some-prop) 'big) (2)

???

Maybe neither of these is the way you're suggesting. Anyone else have
feedback here?

> - MAKE-INSTANCE (oops--"water under the bridge", as they say)
> I believe should never have used keywords.
> The reason is because it's extensible, and conflicting classes
> might choose the same keyword; better to let the package space
> keep them from getting in trouble. (I usually use keywords
> with MAKE-INSTANCE because that's what most people expect, but
> I tend to think it was a wrong choice.

I don't follow you here. Don't most people do this:

(defclass my-class () ()
(...))

(make-instance 'my-class :key-1 val-1 key-2 val-2)

??? It makes complete sense here (to me) that keywords be used for
passing slot values in `make-instance'.

> - A function that returns one of a fixed, finite set of states
> like FIND-SYMBOL's second value does right by returning a keyword.

I like this rule, and I'll stick to it. It seems to be the consensus
too.

dave

Steven D. Majewski

unread,
Jun 1, 1998, 3:00:00 AM6/1/98
to

In article <cxj7m32...@hawk.bu.edu>, David Bakhash <ca...@bu.edu> wrote:
>Hey,
>
>Because my Lisp background is in elisp, I sometimes find myself in a
>situation where my coding style can be improved. Here's a case with
>symbols and keywords.
>
>Say there exists a function in a package called `my-package' which
>examines the global variable *data* and decices what type of data it
>is. Imagine that after examination, it can decide that the data is
>`continuous' or `discrete'.
>

I don't have a general rule, but I have gotten into trouble using
non keyword symbols across packages -- 'my-package:symbol and
'user:symbol are not EQ and sometimes an EQ test defined in one
package but called in another has this subtle bug. Keywords are
all in the keyword package and are EQ, avoiding this bug.


---| Steven D. Majewski (804-982-0831) <sd...@Virginia.EDU> |---
---| Department of Molecular Physiology and Biological Physics |---
---| University of Virginia Health Sciences Center |---
---| P.O. Box 10011 Charlottesville, VA 22906-0011 |---

"Things that go away by themselves can come back by themselves."
-- Microsoft Word Tip of the Day


David Bakhash

unread,
Jun 1, 1998, 3:00:00 AM6/1/98
to

so after all this discussion, it's hard to believe that any function
defined in the `common-lisp' package would want to take a symbol. For
example, the function `concatenate':

(concatenate 'string seq1 seq2)

Why is this never an issue?

dave

Scott L. Burson

unread,
Jun 1, 1998, 3:00:00 AM6/1/98
to

Because packages containing code normally use (i.e., import from) the
COMMON-LISP package, so they'll get the right symbol anyway.

Barry Margolin

unread,
Jun 1, 1998, 3:00:00 AM6/1/98
to

In article <cxj3edp...@hawk.bu.edu>, David Bakhash <ca...@bu.edu> wrote:
>Kent M Pitman <pit...@world.std.com> writes:
>
>> - GET should not use keywords because there are an unbounded number
>> of possible keys.
>
>...should not use keywords for keys or for values? This is ambiguous
>to me.
>
>(defvar x nil)
>
>i.e. are you suggesting against doing:
>
>(setf (get 'x 'some-prop) :big) (1)
>
>or are you saying not to do this:
>
>(setf (get 'x :some-prop) 'big) (2)
>
>???

Kent was referring to (2), although his discussion could also apply to (1)
if the semantics of the SOME-PROP property are that it can reasonable hold
any value.

>> - MAKE-INSTANCE (oops--"water under the bridge", as they say)
>> I believe should never have used keywords.
>> The reason is because it's extensible, and conflicting classes
>> might choose the same keyword; better to let the package space
>> keep them from getting in trouble. (I usually use keywords
>> with MAKE-INSTANCE because that's what most people expect, but
>> I tend to think it was a wrong choice.
>
>I don't follow you here. Don't most people do this:
>
>(defclass my-class () ()
> (...))
>
>(make-instance 'my-class :key-1 val-1 key-2 val-2)
>
>??? It makes complete sense here (to me) that keywords be used for
>passing slot values in `make-instance'.

Yes, this is what most people do, but that doesn't make it good practice.
Consider:

(in-package "P1")

(defclass my-class ()
(slot-1 :initarg :slot-1))

(in-package "P2")

(defclass my-subclass (my-class)
(slot-1 :initarg :slot-1))

Now you have two different slots, P1::SLOT-1 and P2::SLOT-2, that both get
initialized using the :SLOT-1 initarg. The package system allows you to
distinguish the slots, but the keyword package is a shared resource and
collisions can occur. If you instead wrote:

(in-package "P1")

(defclass my-class ()
(slot-1 :initarg slot-1))

(in-package "P2")

(defclass my-subclass (my-class)
(slot-1 :initarg slot-1))

you could then do:

(make-instance 'my-subclass 'p1::slot-1 val-1 'p2::slot-1 val-2)

>> - A function that returns one of a fixed, finite set of states
>> like FIND-SYMBOL's second value does right by returning a keyword.
>
>I like this rule, and I'll stick to it. It seems to be the consensus
>too.

For people familiar with other languages, another way to phrase this rule
of thumb keywords are appropriate in cases where you might use a C enum or
a Pascal SET.

Erik Naggum

unread,
Jun 1, 1998, 3:00:00 AM6/1/98
to

* David Bakhash

| so after all this discussion, it's hard to believe that any function
| defined in the `common-lisp' package would want to take a symbol. For
| example, the function `concatenate':
|
| (concatenate 'string seq1 seq2)
|
| Why is this never an issue?

because in this particular case, COMMON-LISP:STRING names the type of
object CONCATENATE should build and return. a keyword would be
inappropriate in naming types.

#:Erik
--
"Where do you want to go to jail today?"
-- U.S. Department of Justice Windows 98 slogan

Kent M Pitman

unread,
Jun 2, 1998, 3:00:00 AM6/2/98
to

Barry Margolin <bar...@bbnplanet.com> writes:

> For people familiar with other languages, another way to phrase this rule
> of thumb keywords are appropriate in cases where you might use a C enum or
> a Pascal SET.

Interesting. I've on other occasions noticed that enum types were like keywords.
Hadn't really thought about it in relation to this particular situation.

[And thanks for clarifying my other remarks. Sure was nice not to have to bother.]

Howard R. Stearns

unread,
Jun 2, 1998, 3:00:00 AM6/2/98
to

Barry Margolin wrote:
> ...

> &key y should be considered a shorthand for &key ((:y y)). It's doing two
> things: naming the local variable that holds the parameter, and naming the
> keyword used by the caller to pass it. Normally, the latter is the keyword
> form of the former, but the expanded syntax allows you to override this.
> ...

In fact, I try to use the term "named arguments" rather than "keyword
arguments".

Steele gives an example of using a non-keyword symbol as a named
argument that functions as a "secret password".

(I know I'm going to regret this...)

In Eclipse, the question of keywords vs. quoted symbols came up for
providing &key extensions to documented ANSI CL functions.

Some ANSI functions (such as DIRECTORY) explicitly document that
implementations might define additional &key arguments. The
documentation for most functions don't say anything.

If an implementation defines additional &key arguments to some ANSI
function that does not explicitly allow them, should the argument names
be in the keyword package or some implementation-specific package?

I suppose some might argue that an implementation should never do this,
but I think every implementation does. Consider, for example,
COMPILE-FILE and LOAD, which often take additional
implementation-specific named arguments.

We chose to try to limit the damage by using symbols in the ECLIPSE
package to name &key arguments that are eclipse-specific extensions --
much to the surprise and grumbling of our customers!

The thinking is that, extrapolating from Kent's terminology, there is an
unbounded number of implementations, each with their own extensions.
Although only one implementation can be running at a time, "portable"
code could unintentionally clash between implementations that happen to
use the same argument name for different purposes. Using
implementation-specific packages makes this less likely (or more
specifically, it makes it more likely to be caught at read-time when
begining a port).

Any comments? (I'm now running for the exit....)

Scott L. Burson

unread,
Jun 3, 1998, 3:00:00 AM6/3/98
to

Howard R. Stearns wrote:
>
> If an implementation defines additional &key arguments to some ANSI
> function that does not explicitly allow them, should the argument names
> be in the keyword package or some implementation-specific package?
>
> I suppose some might argue that an implementation should never do this,
> but I think every implementation does. Consider, for example,
> COMPILE-FILE and LOAD, which often take additional
> implementation-specific named arguments.

I don't know anyone who would argue that implementations shouldn't feel
free to provide additional keyword arguments to system functions.

> We chose to try to limit the damage by using symbols in the ECLIPSE
> package to name &key arguments that are eclipse-specific extensions --
> much to the surprise and grumbling of our customers!
>
> The thinking is that, extrapolating from Kent's terminology, there is an
> unbounded number of implementations, each with their own extensions.
> Although only one implementation can be running at a time, "portable"
> code could unintentionally clash between implementations that happen to
> use the same argument name for different purposes. Using
> implementation-specific packages makes this less likely (or more
> specifically, it makes it more likely to be caught at read-time when
> begining a port).

Well, I see what you're saying. I don't know if that's valuable enough
to make up for the syntactic inelegance, but it could be. And it
certainly is in the same spirit as using non-keyword symbols as CLOS
"initargs".

Maybe you could set things up so either the keyword or non-keyword
symbols are accepted, leaving the choice of which to use to your
customers.

Funcall of Nil

unread,
Jun 3, 1998, 3:00:00 AM6/3/98
to

Another thought:

Keyword-binding style is essential if you are instantiating large,
sophisticated objects that are performing time-critical operations. It
is helpful when you have a multitude of potential/optional inputs for a
particular object. It is also necessary for readibility. I run batch
jobs which are creating many instances of solid objects which require
the generation of an enormous number of surface object instances, and
cannot afford to have even a single op puke during the process.
Keyword-binding helps me catch problems at compile-time.

fon

Barry Margolin

unread,
Jun 3, 1998, 3:00:00 AM6/3/98
to

In article <357475...@elwood.com>,

Howard R. Stearns <how...@elwood.com> wrote:
>If an implementation defines additional &key arguments to some ANSI
>function that does not explicitly allow them, should the argument names
>be in the keyword package or some implementation-specific package?

Traditionally, the extensions have been put in the keyword package. Until
CLtL2 and ANSI CL, it wasn't even legitimate to use non-keyword symbols to
identify named arguments. We recognized the need for this during the
design of CLOS, and expanded it to cover all keyword arguments.

>I suppose some might argue that an implementation should never do this,
>but I think every implementation does. Consider, for example,
>COMPILE-FILE and LOAD, which often take additional
>implementation-specific named arguments.
>

>We chose to try to limit the damage by using symbols in the ECLIPSE
>package to name &key arguments that are eclipse-specific extensions --
>much to the surprise and grumbling of our customers!
>
>The thinking is that, extrapolating from Kent's terminology, there is an
>unbounded number of implementations, each with their own extensions.
>Although only one implementation can be running at a time, "portable"
>code could unintentionally clash between implementations that happen to
>use the same argument name for different purposes. Using

Any code that uses one of these extensions is not really portable, is it?

>implementation-specific packages makes this less likely (or more
>specifically, it makes it more likely to be caught at read-time when
>begining a port).

This is a reasonable attitude.

The CL standard doesn't mandate this approach because its emphasis is on
the behavior of conforming programs. All of the restrictions on package
contents are designed to prevent unintended clashes between user programs
loaded into a shared environment, or to prevent implementation extensions
from causing nonstandard behavior in conforming programs. Any program that
uses implementation-specific extensions to standard functions is outside
the scope of this protection, so it doesn't really matter in the design of
the standard whether these extensions are named using keywords or ordinary
symbols.

Actually, there's one way in which implementation extensions can become
apparent to conforming code: :ALLOW-OTHER-KEYS T. This is intended for
user-defined functions that accept named arguments that are a superset of
the arguments accepted by some other function (often a standard function).
The function gathers the arguments in an &REST list, then does (apply
#'<standard-function> ... :allow-other-keys t rest-arg). If the user's
function uses a keyword argument that matches the extension to the standard
function, you have a potentially bad conflict.

So I think you're quite right in using symbols in your own package to name
these extensions.

0 new messages