> Is there a CLOS function for copying a CLOS object so that you end up > with two clones that are thereafter independent?
No. There is no way to define "copy" such that this can be done without serious intervention from the creator of the class. E.g., if you have a symbol as the value of a slot, do you want to copy the symbol, and if you copy the symbol, do you want to copy the package? If you do copy the package, do you want to copy _all_ its symbols? Obviously, this leads to a copy of the entire Lisp world in no time (in practice it will much more time). You are better off with the Unix system call "fork". This is not normally what people think of when they want a copy of an object, however.
If you believe that a shallow copy is the right default choice in sharp contrast to the very deep copy described above, consider an object that points to itself with one of its slots. A shallow copy would not make the "clones" independent at all, they would be like Siamese twins, and you would likely wreak havoc if you believed they _were_ independent.
In the Common Lisp world, we do not generally believe in functions that work "most of the time", but look for the border conditions that make it necessary to point out when it works and when it does not. A general "copy constructor" is not well-defined enough to make this possible.
Besides, there is so much going on in CLOS objects that even if you succeed in making a copy, you may have broken many significant other things that depended on object identity in ways the object may not know about. See also the desire to compare two objects for anything other than object identity. Those who say it can be done are simply wrong, because the information necessary to accomplish this seemingly simple task is not specified by the language. It is in the _intent_ of the programmer, not the _expression_ of that intent. Until we find a way to write down the intent directly, we can only deal with expressions of intent in our programming languages.
In other words, the PhD-level answer is "create a language such that it can automatically construct a copy constructor that always does the right thing". It is simpler to write the code for your own copying function.
#:Erik -- There is nothing in this message that under normal circumstances should cause Barry Margolin to announce his moral superiority over others, but one never knows how he needs to behave to maintain his belief in it.
> Is there a CLOS function for copying a CLOS object so that you end up with > two clones that are thereafter independent?
Short answer: No.
Longer answer: Partly because it isn't clear what you mean by "thereafter independent". If the filler of a slot were an object, should that object be recursively copied? If so, then you get into problems with circular object graphs. If not, then a change to the internal state of that filler would be reflected in the internal state of the filler of the copy as well. That might not be independent enough for you. What about if two slots point to the same filler? Should they be copied?
Basically the problem is that the meaning of a copy requires knowledge of the semantics of the data structure which are not captured in the programming language. It is thus nearly impossible to get right in general.
The above is a concise summary of some of the arguments in the Lisp community about why there is no such utility. Kent Pitman has a more detailed and well-reasoned (published) response to this issue.
******
Notwithstanding those arguments, an shallow instance copy would IMHO be useful in enough simple cases to be worth adding to the standard....
-- Thomas A. Russ, USC/Information Sciences Institute t...@isi.edu
Amanda Stent <st...@cs.rochester.edu> writes: > Is there a CLOS function for copying a CLOS object so that you end up with > two clones that are thereafter independent?
Fernando <f...@wanadoo.es> writes: > On 27 Jul 2001 16:41:11 -0400, Amanda Stent <st...@cs.rochester.edu> wrote:
> >Is there a CLOS function for copying a CLOS object so that you end up with > >two clones that are thereafter independent?
> Not really, there's no automagic way of making deep copies of objects (in CLOS > or any other OOP).
Is it really? Now in Eiffel I wrote deep_copy and got it. So probably I'm missing something.
> PS BTW, everytime I thought I had the need for a _deep_ copy, I was wrong... > If you're coming from a C++ background, remember that in Lisp (because of the > GC) copying objects isn't necesary to make sure they are 'deleted' at he right > time: an object doesn0t need to 'own' another one in order to delete >it.
I have a somewhat larger Eiffel program and in it there is exactly one deep_copy. I tried to get away without it but unsuccessfull. I do think that a deep_copy is a very useful thing, as are other facilites. Too often used it could probably indicate a "structural" problem.
* Friedrich Dominicus wrote: > Fernando <f...@wanadoo.es> writes: >> Not really, there's no automagic way of making deep copies of objects (in CLOS >> or any other OOP). > Is it really? Now in Eiffel I wrote deep_copy and got it. So probably > I'm missing something.
Would your automatic deep copier deal correctly with this class:
Tim Bradshaw <t...@cley.com> writes: > * Friedrich Dominicus wrote: > > Fernando <f...@wanadoo.es> writes:
> >> Not really, there's no automagic way of making deep copies of > objects (in CLOS > >> or any other OOP). > > Is it really? Now in Eiffel I wrote deep_copy and got it. So probably > > I'm missing something.
> Would your automatic deep copier deal correctly with this class:
If I do a deep-clone all assertions have to be fullfilled for the constraints too.
So If I have a a : FIFO[FOO]; b : FIFO[FOO];
I got a full copy with b := a.deep_clone;
So I can be sure that the constraint tail = queue.item(count) will hold.
BTW I do think that one of the next addings to Common Lisp should be features for Design by Contract. I know that there is a package for it, anyway that should be standardized. Tools should be used to extrac a) the documentation (as it is done nicely in Common Lisp) b) the contracts (which are lacking in Common Lisp)
Friedrich Dominicus <fr...@q-software-solutions.com> writes: > ... I have a somewhat larger Eiffel program and in it there is > exactly one deep_copy. I tried to get away without it but > unsuccessfull. I do think that a deep_copy is a very useful thing, > as are other facilites. Too often used it could probably indicate a > "structural" problem.
I wish I could explain the depths of my pain when I hear the term "deep copy". If you think "data rape", you're close.
Being itself semantically meaningless, I can't imagine there could possibly be a correct application of it unless in a universe where the programmer had total world knowledge of the representation of all objects that could be run through his programmer and was merely saying "deep copy will in this limited case coincidentally and without damage satisfy the need for a semantically meaningful copy". In the presence of no such total world knowledge and total representational knowledge, I just don't see how it can ever be meaningful/right.
Friedrich Dominicus <fr...@q-software-solutions.com> writes: > Tim Bradshaw <t...@cley.com> writes:
> > * Friedrich Dominicus wrote: > > > Fernando <f...@wanadoo.es> writes:
> > >> Not really, there's no automagic way of making deep copies of > > objects (in CLOS > > >> or any other OOP). > > > Is it really? Now in Eiffel I wrote deep_copy and got it. So probably > > > I'm missing something.
> > Would your automatic deep copier deal correctly with this class:
> > (hint: in order to work, the copier has to ensure that if the QUEUE > > slot is non-NIL then QUEUE-TAIL must be the last cons in QUEUE). > hint. in Eiffel one specfied the characteristics either by Pre or > Postconditions or invariants. You class would probably look like this > in Eiffel
> If I do a deep-clone all assertions have to be fullfilled for the > constraints too.
> So If I have a > a : FIFO[FOO]; > b : FIFO[FOO];
> I got a full copy with > b := a.deep_clone;
> So I can be sure that the constraint tail = queue.item(count) will > hold.
Doesn't this rely on your constraint language to represent all possible intentional truths about the real world which need to be held invariant, rather than merely all truths you have programmed.
This seems to place an unreasonable burden on the programmer of the initial application. Suppose the queue is the queue to enter Buckingham palace and cannot be cloned? Suppose it's one of several (but not an infinite number of) queues that can be cloned in a finite-world model? Suppose there is a complex inter-relationship where there can be only one of someting per n of something else but when something is cloned, the matter around it rearranges to keep things in balance? How much of this does the person who doesn't realize "clone" will be done have to preprogram to have a correct library and how much do they do as a burden of writing clone? I claim that correct code should explicitly offer a method to clone something if it wants to and the absence of something that says you've done it correctly should mean you don't know how; making the default the other way around leads you with a big burden of proof. Further, I also claim that a correct copying requires knowing the purpose for which you are copying. The same object has multiple (even multiple "deep") copy meanings--do I descend leaves? their leaves? The answer comes from the reason for the copy.
> BTW I do think that one of the next addings to Common Lisp should be > features for Design by Contract.
Fine area for research. Things are best not added to the language unless they are stable, commonly implemented (by multiple vendors), etc.
> I know that there is a package for > it, anyway that should be standardized. Tools should be used to extrac > a) the documentation (as it is done nicely in Common Lisp) > b) the contracts (which are lacking in Common Lisp)
Nothing in the language keeps you from building an embedded language that does this, and then showing the wonders of all the programs that can be built on it.
I hear so often that people want to extend the language. For the last 6 months I've been doing tons of programming on my own based on my own notions of extension but not waiting for the language. I just made my own base package and shadowed things as I liked and added symbols I wanted and went to town. It's really fun and requires no one to bless what I've done.
* Friedrich Dominicus wrote: > So I can be sure that the constraint tail = queue.item(count) will > hold.
This would probably work for the class I gave, but it seems to be a non-solution in general. Firstly it requires you to describe, up-front, all the things that must be true about the objects; secondly, and worse, at least in theory, it requires the system to be able to deduce from a constraint an algorithm for ensuring the constraint is true. This is a hard problem!
> This seems to place an unreasonable burden on the programmer of the > initial application.
I disagree. It maybe a burden but it's a very helpful tool. And in fact you can sit down and "first" write down the contracts and make you implementation adhere to thos contracts. This is a really good thing.
>Suppose the queue is the queue to enter Buckingham > palace and cannot be cloned?
Do you think that is a realistic example for QUEUE? If you want to disallow copying have the option to redefine the clone methods. I would discourage doing that, and in fact I never have seen such a thing but in inteface classes (c structures!)
> I claim that > correct code should explicitly offer a method to clone something if it wants > to and the absence of something that says you've done it correctly should > mean you don't know how; making the default the other way around leads > you with a big burden of proof. Further, I also claim that a correct copying > requires knowing the purpose for which you are copying. The same object > has multiple (even multiple "deep") copy meanings--do I descend leaves? > their leaves? The answer comes from the reason for the copy.
I do not understand what you want to say with that.
> > BTW I do think that one of the next addings to Common Lisp should be > > features for Design by Contract.
> Fine area for research. Things are best not added to the language > unless they are stable, commonly implemented (by multiple vendors), > etc.
It is implemented in all Eiffel compilers it is implemented in Sather and there are libraries doing that for (duck) Java and C++. As mentioned before one has done it for Common Lisp too.
> Nothing in the language keeps you from building an embedded language > that does this, and then showing the wonders of all the programs that > can be built on it.
Now, nothing prevents me from doing it, but it's not standard. It is neverthelsss usefull.
Tim Bradshaw <t...@cley.com> writes: > * Friedrich Dominicus wrote:
> > So I can be sure that the constraint tail = queue.item(count) will > > hold.
> This would probably work for the class I gave, but it seems to be a > non-solution in general.
Now it works in general for Eiffel.
> Firstly it requires you to describe, > up-front, all the things that must be true about the objects; > secondly, and worse, at least in theory, it requires the system to be > able to deduce from a constraint an algorithm for ensuring the > constraint is true. This is a hard problem!
Why? You as a programmer have to assure that constraints are obeyed. If you mess it up an Exception is raised. The constraint you were mentoning can be handeled that way and it's specified and it's computer provable. So I can't see where the problem is whith specifying such things if you know them.
> Friedrich Dominicus <fr...@q-software-solutions.com> writes:
> > ... I have a somewhat larger Eiffel program and in it there is > > exactly one deep_copy. I tried to get away without it but > > unsuccessfull. I do think that a deep_copy is a very useful thing, > > as are other facilites. Too often used it could probably indicate a > > "structural" problem.
> I wish I could explain the depths of my pain when I hear the term > "deep copy". If you think "data rape", you're close.
I suggest you read Object oriented software construction and have a look into either Eiffel and/or Sather. If you can't live with how it is specified there, now that's up to you. But just because it hurts you does not mean it does not work.
> Being itself semantically meaningless, I can't imagine there could > possibly be a correct application of it unless in a universe where > the programmer had total world knowledge of the representation of all > objects that could be run through his programmer and was merely saying > "deep copy will in this limited case coincidentally and without damage > satisfy the need for a semantically meaningful copy". In the presence > of no such total world knowledge and total representational knowledge, > I just don't see how it can ever be meaningful/right.
Are you kidding. We are talking about objects. So a compiler should be able to figure out how a object is constructed.
* Friedrich Dominicus wrote: > Why? You as a programmer have to assure that constraints are > obeyed. If you mess it up an Exception is raised. The constraint you > were mentoning can be handeled that way and it's specified and it's > computer provable. So I can't see where the problem is whith > specifying such things if you know them.
Yes, I have to ensure the constraints are obeyed. To do that I may have to write substantial, non-trivial code. Knowing the constraint is *not* the same thing as being able to ensure it is satisfied mechanically.
In one limit case the constraint can be an arbitrary predicate on the object: your proposed deep-copy function now has to somehow automagically ensure that the copy satisfies this same predicate. In finite time, too.
In another limit case the constraint may be that the object corresponds with various real-world objects: copying it would logically require copying them too. That may be hard.
Obviously there are useful cases where these hairy constraints are not needed, and your system will work there. This is OK as far as it goes, but it is *not* a general deep-copy: there *is no useful notion* of a general deep copy. This is what we are trying to say.
This is number 2 in Bradshaw's list of computer science cretinisms: using abstractions which don't exist.
> Yes, I have to ensure the constraints are obeyed. To do that I may > have to write substantial, non-trivial code. Knowing the constraint > is *not* the same thing as being able to ensure it is > satisfied mechanically.
Now that is true, but for that the constraints are "build-in" into Eiffel, please don't forget that we are talking about a language where "the world" is "more or less static".
> In one limit case the constraint can be an arbitrary predicate on the > object: your proposed deep-copy function now has to somehow > automagically ensure that the copy satisfies this same predicate. In > finite time, too.
Now if Eiffel if you do an deep_copy all elements are recursively copied. So if you have a last element in a and make a deep_copy you'll have a last element in b and the complete list too and if you the invariant is checked it used the copied list and the last element to "check" if the constraint is true.
> Obviously there are useful cases where these hairy constraints are not > needed, and your system will work there. This is OK as far as it > goes, but it is *not* a general deep-copy: there *is no useful notion* > of a general deep copy. This is what we are trying to say.
I do think that a deep copy makes sense in Eiffel. As I told before it's quite a static language. I do not know how it could work in a dynamic language like Common Lisp though.
Friedrich Dominicus <fr...@q-software-solutions.com> writes: > Tim Bradshaw <t...@cley.com> writes:
> > * Friedrich Dominicus wrote: > > > Fernando <f...@wanadoo.es> writes:
> > >> Not really, there's no automagic way of making deep copies of > > objects (in CLOS > > >> or any other OOP). > > > Is it really? Now in Eiffel I wrote deep_copy and got it. So probably > > > I'm missing something.
> > Would your automatic deep copier deal correctly with this class:
> > (hint: in order to work, the copier has to ensure that if the QUEUE > > slot is non-NIL then QUEUE-TAIL must be the last cons in QUEUE). > hint. in Eiffel one specfied the characteristics either by Pre or > Postconditions or invariants. You class would probably look like this > in Eiffel
PS: for the record, I have read OOSC 2nd. Edition and I do not consider it the towering master piece you seem to make of it. It's a useful debating trick though. "Go read this 1000+ page book and then we'll talk further"
-- Lieven Marchand <m...@wyrd.be> You can drag any rat out of the sewer and teach it to get some work done in Perl, but you cannot teach it serious programming. Erik Naggum
Friedrich Dominicus <fr...@q-software-solutions.com> writes: > Tim Bradshaw <t...@cley.com> writes:
> > * Friedrich Dominicus wrote: > > > Fernando <f...@wanadoo.es> writes:
> > >> Not really, there's no automagic way of making deep copies of > > objects (in CLOS > > >> or any other OOP). > > > Is it really? Now in Eiffel I wrote deep_copy and got it. So probably > > > I'm missing something.
> > Would your automatic deep copier deal correctly with this class:
> > (hint: in order to work, the copier has to ensure that if the QUEUE > > slot is non-NIL then QUEUE-TAIL must be the last cons in QUEUE). > hint. in Eiffel one specfied the characteristics either by Pre or > Postconditions or invariants. You class would probably look like this > in Eiffel
> If I do a deep-clone all assertions have to be fullfilled for the > constraints too.
> So If I have a > a : FIFO[FOO]; > b : FIFO[FOO];
> I got a full copy with > b := a.deep_clone;
> So I can be sure that the constraint tail = queue.item(count) will > hold.
> BTW I do think that one of the next addings to Common Lisp should be > features for Design by Contract. I know that there is a package for > it, anyway that should be standardized. Tools should be used to extrac > a) the documentation (as it is done nicely in Common Lisp) > b) the contracts (which are lacking in Common Lisp)
This is all nice and good. However, you are just guaranteed that the invariant holds (or not). It does not ease your job to actually write a "deep_copy" function that applies to the Queue (AFAIU).
As an alternative, you can say (and maybe that is the case in Eiffel) that le language (or language library) gives you the "deep_copy" operation and that its use on the Queue above will raise an error somewhere (either at compile time or at run time: I just do not know).
The bottom line does not change. Deep Copy is too tricky to be defined correctly in a completely general term.
Cheers
-- Marco Antoniotti ======================================================== NYU Courant Bioinformatics Group tel. +1 - 212 - 998 3488 719 Broadway 12th Floor fax +1 - 212 - 995 4122 New York, NY 10003, USA http://bioinformatics.cat.nyu.edu "Hello New York! We'll do what we can!" Bill Murray in `Ghostbusters'.
It would be interesting to see an automatically generated `deep-copy' for this. My code to copy queues would be this:
(defgeneric copy-queue (queue))
(defmethod copy-queue ((queue queue)) (let ((new (make-instance (class-of queue)))) (setf (slot-value new 'queue) (copy-list (slot-value queue 'queue)) (slot-value new 'queue-tail) (last (slot-value new 'queue))) new))
Note that the underlying list is only shallowly copied: one could argue that this is not therefore a deep copy: so be it, but it's the copy you *want* in almost all cases.
> Is there a CLOS function for copying a CLOS object so that you end up with > two clones that are thereafter independent?
> Thanks in advance.
As many here have told you, no. However, you could make a clonable mixin that looked at the metaclass information to do an instantiation followed by a shallow copy. This would allow you to do what is done in many smalltalk implementations: The generic copy method is a shallow copy followed by a post copy method. The post copy method is then extended to do deeper copies as needed by the object based on its class. In Lisp it's even easier. Rather than defining a separate post copy method, just define an :after method for copy dispatched on the given object's class.
And, as many have said here, the "copy concept" may be more tricky than it seems. Besides the issues of desired copy depth, there are also issues with objects that hold native resources, pointers to large structures (which you may prevent from bing GC'ed), and other nefarious situations. Be careful that you're doing what you need to and be sure to do it only where you need to.
> It would be interesting to see an automatically generated `deep-copy' > for this. My code to copy queues would be this:
Now you copy is a Lisp solution and uses the facilities from Common Lisp which is ok. I've used the Eiffel facilities in Eiffel and will not "re-write" Lisp in Eiffel. If the behavior of my program is queueish it's done for me. Because we are discussing Common Lisp here I better do not post a Eiffel-ish solution here. Just in Eiffel it is guranteed that using deep_copy yield an independent object and it is not a shallow copy. If you want to see a Eiffel solution you can check out the existing Eiffel libraries or if you want we can talk about it in comp.lang.misc.
> Note that the underlying list is only shallowly copied: one could > argue that this is not therefore a deep copy: so be it, but it's the > copy you *want* in almost all cases.
Now that does disturb me a bit, just that you want it usually does not mean you want it always to be it that way. Here is an Eiffel Queue and a class in which you can see that:
feature -- Access queue (new_item : G) is -- put item at front do store.put_front (new_item); ensure item_put_at_front: store.first = new_item; end;
item : G is require not_empty: not empty; do Result := store.last; ensure Result = store.last; end;
remove is -- remove the last element require not_empty : not empty; do store.finish; store.remove; ensure removed: store.count = old store.count -1; end;
empty : BOOLEAN is do Result := store.empty; end;
feature {NONE} -- Implementation store : TWO_WAY_LIST [G]
invariant item_constraint: not empty implies item.is_equal(store.last);
end -- class MY_QUEUE
class QUEUE_TEST
creation make
feature a : MY_QUEUE[ARRAY[INTEGER]]; b : MY_QUEUE[ARRAY[INTEGER]]; c : like a;
make is do !!a.make; a.queue(<<1,2>>); print_item (a.item); a.queue(<<3,4>>); print_item(a.item);
b := a.clone(a); print_queue (b, "-------------------%Nb= "); make_a; b := a.clone(a); a.remove; print_queue(a, "a= "); make_a; b := a.clone(a); a.remove; print_queue(b, "b= "); make_a; b := a.deep_clone(a); a.remove; print_queue(b, "b="); end;
make_a is do !!a.make; a.queue(<<1, 2>>); a.queue(<<3,4>>); end;
print_queue (queue : like a; msg: STRING) is do io.put_string(msg); io.new_line; from until queue.empty loop print_item(queue.item); queue.remove; end end;
print_item (arr_i: ARRAY[INTEGER]) is local i : INTEGER; do from i := arr_i.lower; until i > arr_i.upper loop io.put_string("arr("); io.put_integer(i); io.put_string(") = "); io.put_integer(arr_i.item(i)); if i < arr_i.upper then io.put_string(","); end; i := i + 1; end; io.new_line; end; end -- class QUEUE_TEST
> As an alternative, you can say (and maybe that is the case in Eiffel) > that le language (or language library) gives you the "deep_copy" > operation and that its use on the Queue above will raise an error > somewhere (either at compile time or at run time: I just do not
know).
deep_copy is defined in Eiffel and it will not raise an error, because all constraints must hold for a copy too.
> The bottom line does not change. Deep Copy is too tricky to be > defined correctly in a completely general term.
it is defined in Eiffel and I posted an example. I do not care if it's tricky to implement. I use it.
* I wrote: > Note that the underlying list is only shallowly copied: one could > argue that this is not therefore a deep copy: so be it, but it's the > copy you *want* in almost all cases.
I thought about this last night, and I'd kind of like to set it as a challenge to anyone who wants to defend automatically-generated copy methods.
If I have some object which represents a queue of other objects, then a useful (I get to define useful here) copy of this object should be a queue of the same, identical objects, *not* a queue of copies of those objects. Given the implementation of a queue I gave this requires an interesting combination of copy-preserving-sharing (for the underlying list of items and the tail pointer) with no copy of the members of the list.
I would like to see a system that can express - in some declarative way such as a constraint language - this notion of copying a queue.
* Friedrich Dominicus wrote: > If you want to see a Eiffel solution you can check out > the existing Eiffel libraries or if you want we can talk about it in > comp.lang.misc. > [...] > indexing > description: "Objects that ..." > author: "" > date: "$Date: 1999/01/12 01:57:05 $" > revision: "$Revision: 1.3 $" > [...]
I'm afraid I find this trick of failing to answer any specific problems raised, suggesting the question should be discussed in a different newsgroup and quoting extended examples in a formalism that it's unreasonable to expect people here to understand pretty irritating. If you want to discuss deep copying do it in Lisp or English, and try and actually answer the questions you are asked rather than evading them.
I don't have any more time to deal with this kind of thing.
Tim Bradshaw <t...@cley.com> writes: > * Friedrich Dominicus wrote:
> > If you want to see a Eiffel solution you can check out > > the existing Eiffel libraries or if you want we can talk about it in > > comp.lang.misc. > > [...]
> I'm afraid I find this trick of failing to answer any specific > problems raised, suggesting the question should be discussed in a > different newsgroup and quoting extended examples in a formalism that > it's unreasonable to expect people here to understand pretty > irritating.
Now and I find it irritating to just state a deep_copy can't work. As I pointed out I will discuss that in another group. But it was you and other which just said a deep_copy does not make sense. Now as I pointed out it's defined and it has worked in Eiffel of years now. Anyway I will shut up on this because it's obvious that not it's the wront group and the wrong discussion partner obviously.
> I don't have any more time to deal with this kind of thing.
Great then do not state such things as "it can't give a deep_copy"
In article <87zo9m4lgf....@frown.here>, Friedrich Dominicus <fr...@q-software-solutions.com> wrote:
>[...] >Why? You as a programmer have to assure that constraints are >obeyed. If you mess it up an Exception is raised. The constraint you >were mentoning can be handeled that way and it's specified and it's >computer provable. So I can't see where the problem is whith >specifying such things if you know them.
So you have to write your own implementation of deep_copy for your QUEUE[T].