Bug in #copy?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

Bug in #copy?

Christoph Thiede

Hi all,


please consider the following snippet and its output:


o := Object new.
o addDependent: 42.
o copy dependents. "#()"

In my opinion, this behavior is misleading for two reasons:

1. #copy is documented as to "answer another instance just like the receiver." From an abstract view of the object, this should include the object's dependents, too. Afaik, DependentsFields is only an implementation detail, but in common sense, the dependents are just another part of the object.

2. If you run exactly the same snippet with a Model instead of an Object (which implements dependencies in a different way under the hood), the copied object will contain the original dependencies, still. I think we should unify this undocumented and unintuitive discrepancy. The Model documentation states it features "fast dependents maintenance" so the behavior should be kept, just optimized.


How do you think about this?

Can we change the Object behavior or could this introduce any unpredictable consequences?

And how could we fix it?


And what about #shallowCopy? Unfortunately, it is called directly by more than a hundred senders in the Trunk image. Because DependentsFields is an implementation detail, I wonder whether #shallowCopy should copy the dependents as well. Should we move the actual primitive method to something like #primShallowCopy and in #shallowCopy, update DependentsFields after calling the primitive? Or do we rather consider #shallowCopy as a low-level method (similar to #veryDeepCopyWith: as apposed to #veryDeepCopy) and call it a code smell to call #shallowCopy from a far-away place?


And LBNL: What should the following snippet output in your opinion?


o1 := Object new.
o1 addDependent: 42.
o2 := o1 shallowCopy. o1 addDependent: 43. o2 dependents asArray.


Should it answer: a) #(), b) #(42), or c) #(42 43)? :D


I am looking forward to your opinions! :-)


Best,

Christoph



Carpe Squeak!
Reply | Threaded
Open this post in threaded view
|

Re: Bug in #copy?

Eliot Miranda-2
Hi Christoph,

On Fri, Aug 21, 2020 at 10:55 AM Thiede, Christoph <[hidden email]> wrote:

Hi all,


please consider the following snippet and its output:


o := Object new.
o addDependent: 42.
o copy dependents. "#()"

In my opinion, this behavior is misleading for two reasons:

1. #copy is documented as to "answer another instance just like the receiver." From an abstract view of the object, this should include the object's dependents, too. Afaik, DependentsFields is only an implementation detail, but in common sense, the dependents are just another part of the object.


I disagree.  Dependents are a mechanism for observing an object. They allow external objects to register interest in a particular instance.  Those observers are not interested in observing the state of arbitrary copies, but of particular objects being observed.  In the history of Smalltalk-80 that I'm aware of from Smalltalk-80 v2 through to VisualWorks 7.x, copying did *not* copy dependents.  In fact, in Model, where for efficiency, Model>>postCopy did self breakDependents.

Before we go further, imagine inspecting some object.  An inspector updates its view of the object being inspected whenever that object issues a changed method.  Now let's inspect a copy of that object.  Wth your implementation both inspectors update whenever either object changes state.  This isn't at all what we want.

  Now for the actual bug.  If you look at copy for Model in Squeak, copy does *not* break dependents, and hence we get the behaviour you (think you) want:


(Model new addDependent: Object new; copy) dependents    =>   a DependentsArray(an Object)

This is a bad bug, and I only discovered it when I looked for a counter-example to your suggestion andfound that Model did (IMO) the *wrong* thing.  So if there is a bug, and I think there is, the fix is:

Model>>postCopy
self breakDependents


2. If you run exactly the same snippet with a Model instead of an Object (which implements dependencies in a different way under the hood), the copied object will contain the original dependencies, still. I think we should unify this undocumented and unintuitive discrepancy. The Model documentation states it features "fast dependents maintenance" so the behavior should be kept, just optimized.


How do you think about this?

Can we change the Object behavior or could this introduce any unpredictable consequences?

And how could we fix it?


And what about #shallowCopy? Unfortunately, it is called directly by more than a hundred senders in the Trunk image. Because DependentsFields is an implementation detail, I wonder whether #shallowCopy should copy the dependents as well. Should we move the actual primitive method to something like #primShallowCopy and in #shallowCopy, update DependentsFields after calling the primitive? Or do we rather consider #shallowCopy as a low-level method (similar to #veryDeepCopyWith: as apposed to #veryDeepCopy) and call it a code smell to call #shallowCopy from a far-away place?


And LBNL: What should the following snippet output in your opinion?


o1 := Object new.
o1 addDependent: 42.
o2 := o1 shallowCopy. o1 addDependent: 43. o2 dependents asArray.


Should it answer: a) #(), b) #(42), or c) #(42 43)? :D


I am looking forward to your opinions! :-)


Best,

Christoph




--
_,,,^..^,,,_
best, Eliot


Reply | Threaded
Open this post in threaded view
|

Re: Bug in #copy?

Jakob Reschke
My first impression also was that dependents should not observe the new object, be it a Model or anything else.

Also Object has some template methods that in the base class do nothing. Since as Object has no state, it makes sense to me that its postCopy method should do nothing.

Eliot Miranda <[hidden email]> schrieb am Fr., 21. Aug. 2020, 21:26:
Hi Christoph,

On Fri, Aug 21, 2020 at 10:55 AM Thiede, Christoph <[hidden email]> wrote:

Hi all,


please consider the following snippet and its output:


o := Object new.
o addDependent: 42.
o copy dependents. "#()"

In my opinion, this behavior is misleading for two reasons:

1. #copy is documented as to "answer another instance just like the receiver." From an abstract view of the object, this should include the object's dependents, too. Afaik, DependentsFields is only an implementation detail, but in common sense, the dependents are just another part of the object.


I disagree.  Dependents are a mechanism for observing an object. They allow external objects to register interest in a particular instance.  Those observers are not interested in observing the state of arbitrary copies, but of particular objects being observed.  In the history of Smalltalk-80 that I'm aware of from Smalltalk-80 v2 through to VisualWorks 7.x, copying did *not* copy dependents.  In fact, in Model, where for efficiency, Model>>postCopy did self breakDependents.

Before we go further, imagine inspecting some object.  An inspector updates its view of the object being inspected whenever that object issues a changed method.  Now let's inspect a copy of that object.  Wth your implementation both inspectors update whenever either object changes state.  This isn't at all what we want.

  Now for the actual bug.  If you look at copy for Model in Squeak, copy does *not* break dependents, and hence we get the behaviour you (think you) want:


(Model new addDependent: Object new; copy) dependents    =>   a DependentsArray(an Object)

This is a bad bug, and I only discovered it when I looked for a counter-example to your suggestion andfound that Model did (IMO) the *wrong* thing.  So if there is a bug, and I think there is, the fix is:

Model>>postCopy
self breakDependents


2. If you run exactly the same snippet with a Model instead of an Object (which implements dependencies in a different way under the hood), the copied object will contain the original dependencies, still. I think we should unify this undocumented and unintuitive discrepancy. The Model documentation states it features "fast dependents maintenance" so the behavior should be kept, just optimized.


How do you think about this?

Can we change the Object behavior or could this introduce any unpredictable consequences?

And how could we fix it?


And what about #shallowCopy? Unfortunately, it is called directly by more than a hundred senders in the Trunk image. Because DependentsFields is an implementation detail, I wonder whether #shallowCopy should copy the dependents as well. Should we move the actual primitive method to something like #primShallowCopy and in #shallowCopy, update DependentsFields after calling the primitive? Or do we rather consider #shallowCopy as a low-level method (similar to #veryDeepCopyWith: as apposed to #veryDeepCopy) and call it a code smell to call #shallowCopy from a far-away place?


And LBNL: What should the following snippet output in your opinion?


o1 := Object new.
o1 addDependent: 42.
o2 := o1 shallowCopy. o1 addDependent: 43. o2 dependents asArray.


Should it answer: a) #(), b) #(42), or c) #(42 43)? :D


I am looking forward to your opinions! :-)


Best,

Christoph




--
_,,,^..^,,,_
best, Eliot



Reply | Threaded
Open this post in threaded view
|

Re: Bug in #copy?

thepete10
fuck off dickweed

On Friday, August 21, 2020, 4:23:16 PM EDT, Jakob Reschke <[hidden email]> wrote:


My first impression also was that dependents should not observe the new object, be it a Model or anything else.

Also Object has some template methods that in the base class do nothing. Since as Object has no state, it makes sense to me that its postCopy method should do nothing.

Eliot Miranda <[hidden email]> schrieb am Fr., 21. Aug. 2020, 21:26:
Hi Christoph,

On Fri, Aug 21, 2020 at 10:55 AM Thiede, Christoph <[hidden email]> wrote:

Hi all,


please consider the following snippet and its output:


o := Object new.
o addDependent: 42.
o copy dependents. "#()"

In my opinion, this behavior is misleading for two reasons:

1. #copy is documented as to "answer another instance just like the receiver." From an abstract view of the object, this should include the object's dependents, too. Afaik, DependentsFields is only an implementation detail, but in common sense, the dependents are just another part of the object.


I disagree.  Dependents are a mechanism for observing an object. They allow external objects to register interest in a particular instance.  Those observers are not interested in observing the state of arbitrary copies, but of particular objects being observed.  In the history of Smalltalk-80 that I'm aware of from Smalltalk-80 v2 through to VisualWorks 7.x, copying did *not* copy dependents.  In fact, in Model, where for efficiency, Model>>postCopy did self breakDependents.

Before we go further, imagine inspecting some object.  An inspector updates its view of the object being inspected whenever that object issues a changed method.  Now let's inspect a copy of that object.  Wth your implementation both inspectors update whenever either object changes state.  This isn't at all what we want.

  Now for the actual bug.  If you look at copy for Model in Squeak, copy does *not* break dependents, and hence we get the behaviour you (think you) want:


(Model new addDependent: Object new; copy) dependents    =>   a DependentsArray(an Object)

This is a bad bug, and I only discovered it when I looked for a counter-example to your suggestion andfound that Model did (IMO) the *wrong* thing.  So if there is a bug, and I think there is, the fix is:

Model>>postCopy
self breakDependents


2. If you run exactly the same snippet with a Model instead of an Object (which implements dependencies in a different way under the hood), the copied object will contain the original dependencies, still. I think we should unify this undocumented and unintuitive discrepancy. The Model documentation states it features "fast dependents maintenance" so the behavior should be kept, just optimized.


How do you think about this?

Can we change the Object behavior or could this introduce any unpredictable consequences?

And how could we fix it?


And what about #shallowCopy? Unfortunately, it is called directly by more than a hundred senders in the Trunk image. Because DependentsFields is an implementation detail, I wonder whether #shallowCopy should copy the dependents as well. Should we move the actual primitive method to something like #primShallowCopy and in #shallowCopy, update DependentsFields after calling the primitive? Or do we rather consider #shallowCopy as a low-level method (similar to #veryDeepCopyWith: as apposed to #veryDeepCopy) and call it a code smell to call #shallowCopy from a far-away place?


And LBNL: What should the following snippet output in your opinion?


o1 := Object new.
o1 addDependent: 42.
o2 := o1 shallowCopy. o1 addDependent: 43. o2 dependents asArray.


Should it answer: a) #(), b) #(42), or c) #(42 43)? :D


I am looking forward to your opinions! :-)


Best,

Christoph




--
_,,,^..^,,,_
best, Eliot




Reply | Threaded
Open this post in threaded view
|

Re: Bug in #copy?

Eliot Miranda-2
I vote to block this person.

_,,,^..^,,,_ (phone)

On Aug 21, 2020, at 3:33 PM, thepete10 <[hidden email]> wrote:


fuck off dickweed

On Friday, August 21, 2020, 4:23:16 PM EDT, Jakob Reschke <[hidden email]> wrote:


My first impression also was that dependents should not observe the new object, be it a Model or anything else.

Also Object has some template methods that in the base class do nothing. Since as Object has no state, it makes sense to me that its postCopy method should do nothing.

Eliot Miranda <[hidden email]> schrieb am Fr., 21. Aug. 2020, 21:26:
Hi Christoph,

On Fri, Aug 21, 2020 at 10:55 AM Thiede, Christoph <[hidden email]> wrote:

Hi all,


please consider the following snippet and its output:


o := Object new.
o addDependent: 42.
o copy dependents. "#()"

In my opinion, this behavior is misleading for two reasons:

1. #copy is documented as to "answer another instance just like the receiver." From an abstract view of the object, this should include the object's dependents, too. Afaik, DependentsFields is only an implementation detail, but in common sense, the dependents are just another part of the object.


I disagree.  Dependents are a mechanism for observing an object. They allow external objects to register interest in a particular instance.  Those observers are not interested in observing the state of arbitrary copies, but of particular objects being observed.  In the history of Smalltalk-80 that I'm aware of from Smalltalk-80 v2 through to VisualWorks 7.x, copying did *not* copy dependents.  In fact, in Model, where for efficiency, Model>>postCopy did self breakDependents.

Before we go further, imagine inspecting some object.  An inspector updates its view of the object being inspected whenever that object issues a changed method.  Now let's inspect a copy of that object.  Wth your implementation both inspectors update whenever either object changes state.  This isn't at all what we want.

  Now for the actual bug.  If you look at copy for Model in Squeak, copy does *not* break dependents, and hence we get the behaviour you (think you) want:


(Model new addDependent: Object new; copy) dependents    =>   a DependentsArray(an Object)

This is a bad bug, and I only discovered it when I looked for a counter-example to your suggestion andfound that Model did (IMO) the *wrong* thing.  So if there is a bug, and I think there is, the fix is:

Model>>postCopy
self breakDependents


2. If you run exactly the same snippet with a Model instead of an Object (which implements dependencies in a different way under the hood), the copied object will contain the original dependencies, still. I think we should unify this undocumented and unintuitive discrepancy. The Model documentation states it features "fast dependents maintenance" so the behavior should be kept, just optimized.


How do you think about this?

Can we change the Object behavior or could this introduce any unpredictable consequences?

And how could we fix it?


And what about #shallowCopy? Unfortunately, it is called directly by more than a hundred senders in the Trunk image. Because DependentsFields is an implementation detail, I wonder whether #shallowCopy should copy the dependents as well. Should we move the actual primitive method to something like #primShallowCopy and in #shallowCopy, update DependentsFields after calling the primitive? Or do we rather consider #shallowCopy as a low-level method (similar to #veryDeepCopyWith: as apposed to #veryDeepCopy) and call it a code smell to call #shallowCopy from a far-away place?


And LBNL: What should the following snippet output in your opinion?


o1 := Object new.
o1 addDependent: 42.
o2 := o1 shallowCopy. o1 addDependent: 43. o2 dependents asArray.


Should it answer: a) #(), b) #(42), or c) #(42 43)? :D


I am looking forward to your opinions! :-)


Best,

Christoph




--
_,,,^..^,,,_
best, Eliot





Reply | Threaded
Open this post in threaded view
|

Re: Bug in #copy?

Christoph Thiede
In reply to this post by Jakob Reschke

Hi all,


thanks for the arguments, I see your point of identity-specific dependency management. I always found this tutorial [1] stating that "in some Smalltalks, Model redefines the method #postCopy to remove the dependents from the shallow #copy". This might be the most elegant solution. I'm going to upload something to the inbox :-)


[1] http://www.bildungsgueter.de/Smalltalk/Pages/MVCTutorial/Pages/Model.htm#:~:text=Copying%20a%20Model


Best,
Christoph

Von: Squeak-dev <[hidden email]> im Auftrag von Jakob Reschke <[hidden email]>
Gesendet: Freitag, 21. August 2020 22:22:53
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] Bug in #copy?
 
My first impression also was that dependents should not observe the new object, be it a Model or anything else.

Also Object has some template methods that in the base class do nothing. Since as Object has no state, it makes sense to me that its postCopy method should do nothing.

Eliot Miranda <[hidden email]> schrieb am Fr., 21. Aug. 2020, 21:26:
Hi Christoph,

On Fri, Aug 21, 2020 at 10:55 AM Thiede, Christoph <[hidden email]> wrote:

Hi all,


please consider the following snippet and its output:


o := Object new.
o addDependent: 42.
o copy dependents. "#()"

In my opinion, this behavior is misleading for two reasons:

1. #copy is documented as to "answer another instance just like the receiver." From an abstract view of the object, this should include the object's dependents, too. Afaik, DependentsFields is only an implementation detail, but in common sense, the dependents are just another part of the object.


I disagree.  Dependents are a mechanism for observing an object. They allow external objects to register interest in a particular instance.  Those observers are not interested in observing the state of arbitrary copies, but of particular objects being observed.  In the history of Smalltalk-80 that I'm aware of from Smalltalk-80 v2 through to VisualWorks 7.x, copying did *not* copy dependents.  In fact, in Model, where for efficiency, Model>>postCopy did self breakDependents.

Before we go further, imagine inspecting some object.  An inspector updates its view of the object being inspected whenever that object issues a changed method.  Now let's inspect a copy of that object.  Wth your implementation both inspectors update whenever either object changes state.  This isn't at all what we want.

  Now for the actual bug.  If you look at copy for Model in Squeak, copy does *not* break dependents, and hence we get the behaviour you (think you) want:


(Model new addDependent: Object new; copy) dependents    =>   a DependentsArray(an Object)

This is a bad bug, and I only discovered it when I looked for a counter-example to your suggestion andfound that Model did (IMO) the *wrong* thing.  So if there is a bug, and I think there is, the fix is:

Model>>postCopy
self breakDependents


2. If you run exactly the same snippet with a Model instead of an Object (which implements dependencies in a different way under the hood), the copied object will contain the original dependencies, still. I think we should unify this undocumented and unintuitive discrepancy. The Model documentation states it features "fast dependents maintenance" so the behavior should be kept, just optimized.


How do you think about this?

Can we change the Object behavior or could this introduce any unpredictable consequences?

And how could we fix it?


And what about #shallowCopy? Unfortunately, it is called directly by more than a hundred senders in the Trunk image. Because DependentsFields is an implementation detail, I wonder whether #shallowCopy should copy the dependents as well. Should we move the actual primitive method to something like #primShallowCopy and in #shallowCopy, update DependentsFields after calling the primitive? Or do we rather consider #shallowCopy as a low-level method (similar to #veryDeepCopyWith: as apposed to #veryDeepCopy) and call it a code smell to call #shallowCopy from a far-away place?


And LBNL: What should the following snippet output in your opinion?


o1 := Object new.
o1 addDependent: 42.
o2 := o1 shallowCopy. o1 addDependent: 43. o2 dependents asArray.


Should it answer: a) #(), b) #(42), or c) #(42 43)? :D


I am looking forward to your opinions! :-)


Best,

Christoph




--
_,,,^..^,,,_
best, Eliot



Carpe Squeak!
Reply | Threaded
Open this post in threaded view
|

Re: Bug in #copy?

Craig Latta
In reply to this post by Eliot Miranda-2

> I vote to block this person.

     Seconded. Done.


-C

--
Craig Latta
Black Page Digital
Berkeley, California
blackpagedigital.com