From: Jerry Boetje on
While working on the :include option, I came across a part of the spec
that is silent on the lexical environment of the included struct.
Here's an example:

Start with
(let ((x 99)) (defstruct foo (a (+ x 2))))

Making a FOO yields
#S(FOO :A 101) ;; as you expect

But if now we make BAR that includes FOO, what happens to the closure?
(defstruct (bar (:include foo)) (b 2))

If I make a BAR, is the result of (make-bar)
#S(BAR :A 101 :B 2)
or
#S(BAR :A NIL :B 2)
or something else...

To make it interesting, define BAR as
(let ((x 42)) (defstruct (bar (:include foo)) (b 2)))

(make-bar) =>
#S(BAR :A 42 :B 2)
or
#S(BAR :A 101 :B 2)

Thoughts?
From: Vassil Nikolov on

On Fri, 9 Oct 2009 10:59:48 -0700 (PDT), Jerry Boetje <jerryboetje(a)mac.com> said:

> While working on the :include option, I came across a part of the spec
> that is silent on the lexical environment of the included struct.

I would interpret

:INCLUDE causes the structure being defined to have the same slots
as the included structure.

to imply sameness of the initforms as well (including preservation
of their lexical environments), rather than a sort of a "macro
expansion" of the included structure definition at the place of
inclusion. I would also argue that the former is the natural
meaning of this kind of inclusion.

---Vassil.


--
"Even when the muse is posting on Usenet, Alexander Sergeevich?"
From: Madhu on

* Vassil Nikolov <snzbpkf6h4c.fsf(a)luna.vassil.nikolov.name> :
Wrote on Fri, 09 Oct 2009 23:51:47 -0400:

| On Fri, 9 Oct 2009 10:59:48 -0700 (PDT), Jerry Boetje said:
|
|> While working on the :include option, I came across a part of the spec
|> that is silent on the lexical environment of the included struct.
|
| I would interpret
|
| :INCLUDE causes the structure being defined to have the same slots
| as the included structure.
|
| to imply sameness of the initforms as well (including preservation
| of their lexical environments), rather than a sort of a "macro
| expansion" of the included structure definition at the place of
| inclusion. I would also argue that the former is the natural
| meaning of this kind of inclusion.

I do not think this interpretation is valid. Because the specification
of DEFSTRUCT specifically says

,----
| The slot default init forms are evaluated in the lexical
| environment in which the defstruct form itself appears and in the
| dynamic environment in which the call to the constructor function
| appears.
|
| For example, if the form (gensym) were used as an initialization form,
| either in the constructor-function call or as the default
| initialization form in defstruct, then every call to the constructor
| function would call gensym once to generate a new symbol.
`----

The first para would indicate ``a sort of a "macro expansion" of the
included structure definition at the place'' that you mentioned is
indeed required.

(unintern 'x)
(let ((x 99)) (defstruct foo (a (+ x 2))))

(make-foo) ; evaluates the initform (+ x 2) in the lexical environment of the
; (defstruct foo) form

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


(make-bar) ; evaluates initform (+ x 2) in the lexical environment of the
; (defstruct bar) form.
; => _Should_ signal ERROR: UNBOUND VARIABLE X

(let ((x 20)) (make-bar)) ; same as above, lexical env of the call to
; make-bar does not affect the initform

(let ((x 20)) (declare (special x)) (make-bar))

;; => #S(BAR :A 22 :B 2) ; the initform (+ x 2) is evaluated in the
; dynamical environment of the call to
; MAKE-BAR
--
Madhu
From: JB at CofC on
On Oct 10, 11:04 am, Ron Garret <rNOSPA...(a)flownet.com> wrote:
> In article <m3iqeno90s....(a)moon.robolove.meer.net>,
>
>
>
>
>
>
> No.  If you have an :include option then there are two defstruct forms
> in question, the one being defined and the :included one, and hence two
> lexical environments potentially in play.
>
> Reasonable people could certainly disagree, but IMHO the following
> behavior is undesirable:
>
> ? (defstruct (baz (:include foo)))
> ;Compiler warnings :
> ;   In MAKE-BAZ: Undeclared free variable X
>
> X?  What X?  I don't see any X.  Do you see an X?
>
> This is even worse IMHO:
>
> ? (make-baz)
> Error: Unbound variable: X
>
> And this is worse still:
>
> ? (defvar x 'some-random-value)
> X
> ? (make-baz)
> #S(BAZ :Z SOME-RANDOM-VALUE)
> ?
>
> Note that the slot that gets initialized to some-random-value is named
> Z, not X.
>
> And if that's not bad enough, here's the capper:
>
> ? (make-baz)
> Error: value SOME-RANDOM-VALUE is not of the expected type NUMBER.
>
> > I think you are introducing ambiguity where there is none.
>
> No, I am pointing out ambiguity where it actually exists.
>
> > There is no confusion in the spec as far as I can see, and I think my
> > examples ought to have clarified that.
>
> It is logically impossible to prove the absence of ambiguity with
> examples.
>
> > I fear again you are simply exploiting possible misunderstandings and
> > confusions
>
> Are you a native English speaker?  What do you think the word
> "ambiguity" means?
>
> rg

Here are some examples of what I think should be the correct behaviors
from my last post... BTW, they all assume that there are no special
variables

1. The one everyone agrees on...

(let ((x 40)) (defstruct foo (a (+ x 2))))

(make-foo)
=> #S(FOO :A 42)
...> this is true wherever the call to MAKE-FOO happens

2. The second where it gets interesting...

(defstruct (bar :include foo) (b 1))

(make-bar)
=> #S(BAR :A 42 :B 1)

3. (let ((x 20)) (defstruct (bar (:include foo)) (b 1)))

(make-bar)
=> #S(BAR :A 42 :B 1)
....> The binding of X occurred within the lexical environment in (1).
The binding of X in this example is irrelevant.

4. (let ((x 20))
(defstruct (bar (:include foo (a :init-form (+ x 10)))) (b 1)))

(make-bar)
=> #S(BAR :A 30 :B 1)
....> In this case, the :include clause redefines the init-form for
slot A.

....Now, here's another twist. What do you get when you try...
Obviously (bar-a (make-bar)) yields 30
But what happens if...
(foo-a (make-bar)) ;; is it 30 or 42?
From: Vassil Nikolov on

On Sat, 10 Oct 2009 09:23:03 -0700 (PDT), JB at CofC <boetjeg(a)gmail.com> said:
> ...
> (let ((x 40)) (defstruct foo (a (+ x 2))))
> ...
> 4. (let ((x 20))
> (defstruct (bar (:include foo (a :init-form (+ x 10)))) (b 1)))
> ...
> ...Now, here's another twist. What do you get when you try...
> Obviously (bar-a (make-bar)) yields 30
> But what happens if...
> (foo-a (make-bar)) ;; is it 30 or 42?

Different initform in BAR, but still the same slot (and having two
slots with the same name is not allowed anyway), so, 30 for both.

---Vassil.


--
"Even when the muse is posting on Usenet, Alexander Sergeevich?"