From: Jeff Dalton
Subject: Refs to earlier slots in defstruct
Date: 
Message-ID: <x2vhrszd0j.fsf_-_@gairsay.aiai.ed.ac.uk>
I used to be able to do stuff like this in a number of different
Common Lisps:

(defstruct (queue (:print-function print-queue))
  (head (list nil))			;a headed list
  (tail head))				;ptr to the last cons in the list

Liquid CL (unlike Lucid!) says "no", which I find to be a major
pain:

> (make-queue)

>>Error: The symbol HEAD has no global value

But, also in Liquid

> ((lambda (&key (head (list nil)) (tail head)) (list head tail)))
((NIL) (NIL))

But defstruct constructors are supposed to work like functions
with keyword parameters.

So is Liquid wrong, or did the standardization process mess things
up somehow?

-- jeff

From: Kent M Pitman
Subject: Re: Refs to earlier slots in defstruct
Date: 
Message-ID: <sfwiunsflx8.fsf@world.std.com>
Jeff Dalton <····@gairsay.aiai.ed.ac.uk> writes:

> I used to be able to do stuff like this in a number of different
> Common Lisps:
> 
> (defstruct (queue (:print-function print-queue))
>   (head (list nil))			;a headed list
>   (tail head))				;ptr to the last cons in the list
> 
> Liquid CL (unlike Lucid!) says "no", which I find to be a major
> pain:
> 
> > (make-queue)
> >>Error: The symbol HEAD has no global value
[...]
> But defstruct constructors are supposed to work like functions
> with keyword parameters.

But not necessarily where you know the internal var names, which
need not be the external key names.

> So is Liquid wrong, or did the standardization process mess things
> up somehow?

I am next to certain I recently re-read a very specific proposal to legitimize
the behavior you want and that it was expressly voted down by X3J13.
I'll try to find the issue reference.
From: Jeff Dalton
Subject: Re: Refs to earlier slots in defstruct
Date: 
Message-ID: <x2af91bx39.fsf@gairsay.aiai.ed.ac.uk>
Kent M Pitman <······@world.std.com> writes:

> Jeff Dalton <····@gairsay.aiai.ed.ac.uk> writes:

> > I used to be able to do stuff like this in a number of different
> > Common Lisps:

> > (defstruct (queue (:print-function print-queue))
> >   (head (list nil))   ;a headed list
> >   (tail head))        ;ptr to the last cons in the list

> > But defstruct constructors are supposed to work like functions
> > with keyword parameters.

> But not necessarily where you know the internal var names, which
> need not be the external key names.

I do not understand that point, I'm afraid.

Is my &key example is not guaranteed to work?  It was:

 ((lambda (&key (head (list nil)) (tail head)) (list head tail)))

> > So is Liquid wrong, or did the standardization process mess things
> > up somehow?
> 
> I am next to certain I recently re-read a very specific proposal to
> legitimize the behavior you want and that it was expressly voted down
> by X3J13.  I'll try to find the issue reference.

That would be great.  I tried to find something in the hyperspec, but
couldn't find anything that was sufficiently explicit.

-- jeff
From: Barry Margolin
Subject: Re: Refs to earlier slots in defstruct
Date: 
Message-ID: <B%r21.11$5r.415397@cam-news-reader1.bbnplanet.com>
In article <··············@gairsay.aiai.ed.ac.uk>,
Jeff Dalton  <····@gairsay.aiai.ed.ac.uk> wrote:
>Kent M Pitman <······@world.std.com> writes:
>> But not necessarily where you know the internal var names, which
>> need not be the external key names.
>
>I do not understand that point, I'm afraid.
>
>Is my &key example is not guaranteed to work?  It was:
>
> ((lambda (&key (head (list nil)) (tail head)) (list head tail)))

The name of the keyword need not be the same as the name of the parameter
variable.  It could be:

((lambda (&key ((:head #:temp1) (list nil)) ((:tail #:temp2) head))
   (let ((head #:temp1)
         (tail #:temp2))
     (list head tail))))

(assume appropriate use of #n# and #n='s to link the gensyms in my
examples).

>> > So is Liquid wrong, or did the standardization process mess things
>> > up somehow?
>> 
>> I am next to certain I recently re-read a very specific proposal to
>> legitimize the behavior you want and that it was expressly voted down
>> by X3J13.  I'll try to find the issue reference.
>
>That would be great.  I tried to find something in the hyperspec, but
>couldn't find anything that was sufficiently explicit.

I think it's DEFSTRUCT-CONSTRUCTOR-SLOT-VARIABLES, whose NOT-BOUND proposal
was approved.

CLtL2 p.474 says, "If the default initialization form is used, it is
evaluated at construction time, but in the lexical environment of the
DEFSTRUCT form in which it appeared."  Earlier on the page there is an
example DEFUN as might be produced by DEFSTRUCT, but it says it's "roughly
as if its definition were" that example; the roughness is in the way the
init-forms would be handled if there were any.  The requirement to evaluate
them in DEFSTRUCT's lexical environment means that they need to be lexical
closures, so the form:

(defstruct s
  (head (list nil))
  (tail head))

would have to expand into something like:

(flet ((#:head-init () (list nil))
       (#:tail-init () head))
  (defun make-s (&key (head (#:head-init))
                      (tail (#:tail-init)))
    (si:make-structure 's head tail))
  ...)

The only time when slot names are visibly used as lambda variable names
that can be referred to in init forms is in BOA constructors.

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Cambridge, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
From: Jeff Dalton
Subject: Re: Refs to earlier slots in defstruct
Date: 
Message-ID: <x2g1iqxn38.fsf@gairsay.aiai.ed.ac.uk>
Barry Margolin <······@bbnplanet.com> writes:

> In article <··············@gairsay.aiai.ed.ac.uk>,
> Jeff Dalton  <····@gairsay.aiai.ed.ac.uk> wrote:
> >Kent M Pitman <······@world.std.com> writes:
> >> But not necessarily where you know the internal var names, which
> >> need not be the external key names.

> >I do not understand that point, I'm afraid.

> >Is my &key example is not guaranteed to work?  It was:

> > ((lambda (&key (head (list nil)) (tail head)) (list head tail)))

> The name of the keyword need not be the same as the name of the parameter
> variable. 

What makes you think that?  (Just talking about my ((lambda ...))
keyword example here.)

> It could be:

> ((lambda (&key ((:head #:temp1) (list nil)) ((:tail #:temp2) head))
>    (let ((head #:temp1)
>          (tail #:temp2))
>      (list head tail))))

> (assume appropriate use of #n# and #n='s to link the gensyms in my
> examples).

What makes you think things can work that way?

CltL 2, pp 79-80 looks like it pretty clearly guarantees the behaviour
I expect.

On p 79, it says "in each paramter specifier must be a name _var_
for the parameter variable".  The name in the parameter specifier
is, then, the name of the parameter variable.  There's nothing
that suggests the parameter variable might instead be #:temp1.

So in (&key (head (list nil)) (tail head)), the parameter variables
are head and tail.

Then, 79-80 it says the parameter specifiers are processed from
left to right, and then describes this in more detail.  For each
parameter specifier ... the parameter variable for that specifier
is bound ...

That should already be clear enough, and it's hard to see any
point to specifying this if the parameter variables might all
be generated temps.

In any case, it's later (p 80) stated explicitly: Whenever any
_initform_ is evaluated for any parameter specifier, that form may
refer to any parameter variable to the left of the specifier in which
the _initform_ appears ...

-- jeff
From: Barry Margolin
Subject: Re: Refs to earlier slots in defstruct
Date: 
Message-ID: <Ngm31.10$wP.254238@cam-news-reader1.bbnplanet.com>
In article <··············@gairsay.aiai.ed.ac.uk>,
Jeff Dalton  <····@gairsay.aiai.ed.ac.uk> wrote:
>Barry Margolin <······@bbnplanet.com> writes:
>
>> In article <··············@gairsay.aiai.ed.ac.uk>,
>> Jeff Dalton  <····@gairsay.aiai.ed.ac.uk> wrote:
>> >Kent M Pitman <······@world.std.com> writes:
>> >> But not necessarily where you know the internal var names, which
>> >> need not be the external key names.
>
>> >I do not understand that point, I'm afraid.
>
>> >Is my &key example is not guaranteed to work?  It was:
>
>> > ((lambda (&key (head (list nil)) (tail head)) (list head tail)))
>
>> The name of the keyword need not be the same as the name of the parameter
>> variable. 
>
>What makes you think that?  (Just talking about my ((lambda ...))
>keyword example here.)

I thought you were saying that it was the parameter list that DEFSTRUCT
would generate.

>In any case, it's later (p 80) stated explicitly: Whenever any
>_initform_ is evaluated for any parameter specifier, that form may
>refer to any parameter variable to the left of the specifier in which
>the _initform_ appears ...

I was talking about DEFSTRUCT init-forms, not lambda-list init-forms.

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