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

Question about &rest, &key, and &allow-other-keys

106 views
Skip to first unread message

rif

unread,
Jan 18, 2004, 1:18:34 PM1/18/04
to

I have a structure with a large number of parameters. I would like to
have a function that gives certain defaults to a few of these members,
allows the caller to specify any members values explicitly, and
accepts the standard constructor defaults for the others.

For instance, I might have:

(defstruct foo a b c d e f g h i)

(defun make-my-foo (&rest params &key (d 10) (g 3) &allow-other-keys)
(apply #'make-foo :d d :g g params))

This works fine, but I'm not clear why I have to explicitly include
the named keywords in the call to apply. In other words, I'm
wondering why

(defun make-my-foo-bad (&rest params &key (d 10) (g 3) &allow-other-keys)
(apply #'make-foo params))

doesn't DTRT, in the sense that it won't be default set d or g. This
is a big frustrating because my member names are much longer than
single characters, and I don't see conceptually why I should have to
include them twice --- once in the lambda list and again in the call
to apply. Conceptually, I think I see how to write a macro that would
define a function like make-my-foo from input like make-my-foo-bad
(haven't tried this yet), but I'm just wondering if I'm missing some
simpler way to DWIW, and also if possible an explanation for why
things work this way.

Cheers,

rif

Peter Seibel

unread,
Jan 18, 2004, 1:32:58 PM1/18/04
to
rif <r...@mit.edu> writes:

> I have a structure with a large number of parameters. I would like to
> have a function that gives certain defaults to a few of these members,
> allows the caller to specify any members values explicitly, and
> accepts the standard constructor defaults for the others.
>
> For instance, I might have:
>
> (defstruct foo a b c d e f g h i)
>
> (defun make-my-foo (&rest params &key (d 10) (g 3) &allow-other-keys)
> (apply #'make-foo :d d :g g params))
>
> This works fine, but I'm not clear why I have to explicitly include
> the named keywords in the call to apply. In other words, I'm
> wondering why
>
> (defun make-my-foo-bad (&rest params &key (d 10) (g 3) &allow-other-keys)
> (apply #'make-foo params))
>
> doesn't DTRT, in the sense that it won't be default set d or g.

I assume you mean you want to be able to say:

(make-my-foo-bad :a 1 :b 2) ; i.e. no :d or :g passed

and have the default values of :d and :g passed to MAKE-FOO via the
APPLY. This doesn't work the way you seem to expect it to because the
&rest argument gathers up the actual parameters to the call. Thus
PARAMS only contains the arguments you pass to MAKE-MY-FOO. The
binding of default values to the named parameters D and G is
completely separate.

However I'm not sure why you don't specify the default values in the
DEFSTRUCT form:

CL-USER(327): (defstruct foo a b c (d 10) e f (g 20) h i)
FOO
(make-foo)
#S(FOO :A NIL :B NIL :C NIL :D 10 :E NIL :F NIL :G 20 :H NIL :I NIL)
CL-USER(329): (make-foo :d 'something-else)
#S(FOO :A NIL :B NIL :C NIL :D SOMETHING-ELSE :E NIL :F NIL :G 20 :H NIL :I NIL)
CL-USER(330):

-Peter

--
Peter Seibel pe...@javamonkey.com

Lisp is the red pill. -- John Fraser, comp.lang.lisp

Erik Naggum

unread,
Jan 18, 2004, 4:36:46 PM1/18/04
to
* rif <r...@mit.edu>

| I have a structure with a large number of parameters. I would like to
| have a function that gives certain defaults to a few of these members,
| allows the caller to specify any members values explicitly, and
| accepts the standard constructor defaults for the others.

In addition to specifying the default values as Peter Seibel mentions,
investigate the :CONSTRUCTOR option to DEFSTRUCT.

--
Erik Naggum | Oslo, Norway

Act from reason, and failure makes you rethink and study harder.
Act from faith, and failure makes you blame someone and push harder.

rif

unread,
Jan 18, 2004, 10:47:17 PM1/18/04
to

OK, thanks for the info.

The reason I don't just put the default arguments into the constructor
for the call is that I need a bunch of different "sets of defaults".
The structure holds a bunch of parameters that control how a
computation goes. In a given experimental setting, there will be a
number of these parameters that take on the same defaults, and then
I'll also need to vary one or two more. So if I have scenarios A and
B and C, I need different sets of defaults for each scenarios, with
the additional ability to specify individual parameters manually. For
this reason, I don't think :CONSTRUCTOR can help me either, as I need
several different sets of defaults.

I can see three possible solutions, and I'd be interested in hearing
which other people might choose and why, or any other ideas.

1. Suck it up and write functions that contain the defaults I need
both in the parameter list and in the apply call, which is what
I'm already doing:

> (defun make-my-foo (&rest params &key (d 10) (g 3) &allow-other-keys)
> (apply #'make-foo :d d :g g params))

2. Write a macro to take a minimal specification of what I want and
generate the functions.

3. Switch to CLOS, make each separate set of defaults a new class
that inherits from the base "control object", with the same fields
but different defaults.

Cheers,

rif

Erik Naggum

unread,
Jan 19, 2004, 12:12:45 AM1/19/04
to
* rif <r...@mit.edu>

| So if I have scenarios A and B and C, I need different sets of
| defaults for each scenarios, with the additional ability to specify
| individual parameters manually. For this reason, I don't think
| :CONSTRUCTOR can help me either, as I need several different sets of
| defaults.

I suggested that you /investigate/ the :CONSTRUCTOR option to DEFSTRUCT
for a reason. Please take a better look at it and notice that it may
be repeated. This really is just what you need.

Tim Bradshaw

unread,
Jan 19, 2004, 8:42:27 AM1/19/04
to
* rif wrote:

> I can see three possible solutions, and I'd be interested in hearing
> which other people might choose and why, or any other ideas.

As well as Erik's solution (which is the best, I think), you can do
the multiple-subclass thing without using all of CLOS's hair:

(defstruct foo
(a 1))

(defstruct (bar (:include foo (a 2))))

Then you can have multiple essentially identical structure classes
with different slot initargs. I really do think using just one class
with multiple constructors is nicer though.

--tim

0 new messages