I have a CLOS object which may contain an arbitrary graph of other objects. This graph may be cyclic. I'm looking for an easy way to deep copy the object. I've been told that generic deep copy might be implementing by writing the object to a stream, and then reading back from that stream. Is this the best way of doing it? Is there another way?
* Courageous <jkras...@san.rr.com> | I have a CLOS object which may contain an arbitrary graph of other | objects. This graph may be cyclic. I'm looking for an easy way to deep | copy the object.
the only easy way out is to write a copier that knows what it's doing. any easier way out would violate "as simple as possible, but no simpler".
| I've been told that generic deep copy might be implementing by writing | the object to a stream, and then reading back from that stream.
you've been told that? what a joker that must have been! how do you think you would proceed to implement readers and writers for the objects? do you think _they_ come in generic versions that do everything right?
(for the languages where you may use a generic version that does it wrong most of the time, which you usuallyq observe if you try, there _is_ the merit to this language design that most "modern programmers" (= dummies) are a lot happier when they something out and see it fail, then do it "right" (= less _obviously_ wrong), than to know it would fail and know they had to do it right from the start.)
implementing a mechanism that avoids descending into cyclic structures is amazingly easy. detection is easy with the rabbit and the hare algorithm. a hash table of copied objects avoids all possible problems to begin with.
> implementing a mechanism that avoids descending into cyclic structures is > amazingly easy. detection is easy with the rabbit and the hare algorithm. > a hash table of copied objects avoids all possible problems to begin with.
In essence, I'm looking for an implementation of this that uses introspection on the objects; I require a generic implementation that knows nothing in specific a-priori about the objects it will deep copy; however, stipulating certain rules on the structure of the objects may be within reason. I know this is possible one way or the other, for I have seen automatic deep copy implemented via code generation in C++. I'm pretty new at lisp; however, so I'm not sure what language-specific pitfalls I will encounter yet.
* Courageous <jkras...@san.rr.com> | I know this is possible one way or the other, for I have seen automatic | deep copy implemented via code generation in C++.
I'm sorry, but you have been tricked. just because it exists for C++ is not in any way, shape, or form an argument for the _correctness_ of the implementation or the algorithm or even the concept to begin with.
| I'm pretty new at lisp; however, so I'm not sure what language-specific | pitfalls I will encounter yet.
you encountered a language-specific pitfall and fell headlong into it when you believed that C++ had an automatic solution. you will get out of this sorry state of affairs when you realize that such automated tools are not able to do their job. the key to understand this is that even in a class for which "deep copy" makes non-zero sense, you don't _always_ want to deep copy _everything_, and if you fail to implement it right, you will in fact destroy valuable information in the copying process.
* Courageous wrote: > I have a CLOS object which may contain an arbitrary graph of > other objects. This graph may be cyclic. I'm looking for an > easy way to deep copy the object. I've been told that > generic deep copy might be implementing by writing the > object to a stream, and then reading back from that stream. > Is this the best way of doing it? Is there another way?
What you want is something more than deep copying -- something I call `graph copying' although it may have a proper name. It copies while keeping track of every object it has copied, and avoiding copying it again in that case. Graph copying is unavoidably expensive because it needs an occurs check.
Common Lisp does not have a generic object copying function because the notion of `copying' is something that is typically application-dependent. Languages that provide one typically provide one that doesn't work most of the time (C++ is a good example) so you end up overriding it most of the time.
For objects that CL knows how to print & read, than it is often possible do a graph copy by printing them to a stream & reading back, but it's almost always better to write an application-specific copier.
Here's a (badly written I think) example of dealing with circularities & sharing for cons trees (only!):
(defun copy-cons-graph (x) (let ((octab (make-hash-table))) (labels ((cp (y) (typecase y (cons (or (gethash y octab) (let ((c (cons nil nil))) ;; the new cons needs to be in OCTAB ;; before we try and descend into it. ;; This seems a pretty clunky way of doing ;; this, but it's sunday night. (setf (gethash y octab) c) (setf (car c) (cp (car y))) (setf (cdr c) (cp (cdr y))) c))) (t y)))) (cp x))))
Finally: I gave a paper on stuff related to this at last year's lugm, I don't know if it's on the web anywhere, but franz's web site would be a good place to look (I will put up a descendent of it at some point, when I have some time).
> you encountered a language-specific pitfall and fell headlong into it > when you believed that C++ had an automatic solution.
I don't believe that C++ has a an automatic solution, rather, I've seen a code generator (for C++) which correctly copies graphs of the objects while keeping referential integrity intact.
> and if you fail to implement it right, > you will in fact destroy valuable information in the copying process.
Never had that happen is all of 3 years using the code generator that I had, which would, in fact, copy wildly crazy and cyclic graphs of objects perfectly correctly. Furthermore, if you look into the world of object oriented databases, you'll find that various oodbs also do this perfectly well.
Perhaps we're having a definition of terms problem?
I can't write a specific graph copier, because I don't have a- priori knowledge of the structure of the objects that I will be copying.
Erik Naggum <e...@naggum.no> wrote: +--------------- | * Courageous <jkras...@san.rr.com> | | I know this is possible one way or the other, for I have seen automatic | | deep copy implemented via code generation in C++. | | I'm sorry, but you have been tricked. just because it exists for C++ is | not in any way, shape, or form an argument for the _correctness_ of the | implementation or the algorithm or even the concept to begin with. +---------------
Indeed. Perhaps it's time once again to drag out Kent Pitman's classic "Parenthetically Speaking" article on the subject:
which addresses the question "Why is there no generic COPY function?" [in Common Lisp], and explains why any so-called "automatic" deep copy is inherently *wrong*.
-Rob
----- Rob Warnock, 41L-955 r...@sgi.com Applied Networking http://reality.sgi.com/rpw3/ Silicon Graphics, Inc. Phone: 650-933-1673 1600 Amphitheatre Pkwy. PP-ASEL-IA Mountain View, CA 94043
As I said in one of my posts, I'd be willing to accept certain structural rules of organization. With this particular code generator, it used the well-defined CORBA IDL (which simply cannot express a function pointer and the like). Likewise, with the ODMG DDL, you have a fairly restricted language of expression.
If you think about it, it's strictly impossible to implement utterly generic graph copy, reflectivity or not (imagine a simple C array of int; how long is it? Dunno).
My second post hinted at this. In C/C++, we use a vector type, and only support that. For objects to persist or be relocatable, they have to be defined using a set of very specific primitives. The arrangement of these primitives will not be known in advance. Cycles may be present and must be (and can be) accounted for.
I'm just looking for a simple way to do this with Lisp. Given Lisp's flexibility, I doubt very highly it would require a code generator, but I've been wrong before.
(BTW, as Franz has a CORBA binding, and presumably this binding does Objects By Value, this would suggest that at least one commercial implementation of what I'm looking for already exists).
* Courageous wrote: > I can't write a specific graph copier, because I don't have a- > priori knowledge of the structure of the objects that I will be > copying.
Look, you can't write a general copier, get over it. Whatever your C++ framework is doing it *isn't* doing general copy. You need to know enough about the objects you want to copy to know what can occur in these objects, how to copy them, what bits should be copied, what is not copiable, what equality means when detecting cycles, and so forth.
If this isn't obvious, think for a few seconds about how a general copier should behave when it comes across a package object, say.
> > I can't write a specific graph copier, because I don't have a- > > priori knowledge of the structure of the objects that I will be > > copying.
> Look, you can't write a general copier, get over it.
Well, actually you *can* write a "general" graph copier if the graph of objects is composed of objects made from a restricted set of primitives.
A tip for some of you folks: one of the keys to successful communication is not to assume that the other person is wrong, but rather to figure out what they're right *for*.
Courageous wrote: > > > I can't write a specific graph copier, because I don't have a- > > > priori knowledge of the structure of the objects that I will be > > > copying.
> > Look, you can't write a general copier, get over it.
> Well, actually you *can* write a "general" graph copier if the > graph of objects is composed of objects made from a restricted > set of primitives.
> A tip for some of you folks: one of the keys to successful > communication is not to assume that the other person is wrong, > but rather to figure out what they're right *for*.
In article <38F14F3F.D6A54...@san.rr.com> posted on Sunday, April 9, 2000 10:47 PM, Courageous <jkras...@san.rr.com> wrote:
>> > I can't write a specific graph copier, because I don't have a- >> > priori knowledge of the structure of the objects that I will be >> > copying.
>> Look, you can't write a general copier, get over it.
> Well, actually you *can* write a "general" graph copier if the > graph of objects is composed of objects made from a restricted > set of primitives.
A general copier that working in specific situations, then :)
-- -> -\-=-=-=-=-=-=-=-=-=-/^\-=-=-=<*><*>=-=-=-/^\-=-=-=-=-=-=-=-=-=-/- <- -> -/-=-=-=-=-=-=-=-=-=/ { Rahul -<>- Jain } \=-=-=-=-=-=-=-=-=-\- <- -> -\- "I never could get the hang of Thursdays." - HHGTTG by DNA -/- <- -> -/- http://photino.sid.rice.edu/ -=- mailto:rahul-j...@usa.net -\- <- |--|--------|--------------|----|-------------|------|---------|-----|-| Version 11.423.999.210020101.23.50110101.042 (c)1996-2000, All rights reserved. Disclaimer available upon request.
On 09 Apr 2000 17:48:09 +0000, "Erik" == Erik Naggum <e...@naggum.no> writes:
Erik> implementing a mechanism that avoids descending into cyclic structures is Erik> amazingly easy.
if slightly non-obvious at first. For those confused, you have two parts of your function step through the elements of a list, one going in single steps, the other in steps of two. If they ever end up in the same element, the list must have been circular.
Erik> detection is easy with the rabbit and the hare algorithm.
Is this an optimized version of the tortoise and the hare algorithm?
Erik> a hash table of copied objects avoids all possible problems to begin with.
but may not scale. Philip
-- Not getting what you want is sometimes a wonderful stroke of luck. --------------------------------------------------------------------------- -- Philip Lijnzaad, lijnz...@ebi.ac.uk | European Bioinformatics Institute,rm A2-24 +44 (0)1223 49 4639 | Wellcome Trust Genome Campus, Hinxton +44 (0)1223 49 4468 (fax) | Cambridgeshire CB10 1SD, GREAT BRITAIN PGP fingerprint: E1 03 BF 80 94 61 B6 FC 50 3D 1F 64 40 75 FB 53
* Courageous wrote: > Well, actually you *can* write a "general" graph copier if the > graph of objects is composed of objects made from a restricted > set of primitives.
Perhaps you should be clear about what you mean by `general'. Earlier you said:
I require a generic implementation that knows nothing in specific a-priori about the objects it will deep copy
which people have interpreted to mean something like, given an object O, return a copy of O when you do not know anything about what O is. But actually, what you really seem to want is something that is very specific, and works on some small set of classes. That is easy to write -- so easy to write that I'm kind of confused why you are asking for it. You just need a notion of equality, and way of telling if you have seen an object equal to the one you are looking at before (if your notion of equality is EQL or something, then Lisp provides this functionality in hash tables, if it is some user-defined predicate you may have to write a small hashtable implementation based on it), and some basic recursive programming.
> A tip for some of you folks: one of the keys to successful > communication is not to assume that the other person is wrong, > but rather to figure out what they're right *for*.
One of the keys to successful communication on this newsgroup is to ask for what you want in reasonably specific terms rather than extremely vague ones.
Courageous wrote: > I have a CLOS object which may contain an arbitrary graph of other > objects. This graph may be cyclic. I'm looking for an easy way to deep > copy the object.
A generic deep copy function is impossible because in order to copy an object, you must know the meaning of the object and how it will be used.
``Why is there no generic COPY function?'' Common Lisp programmers often ask this question. [...]
Programmers want a generic COPY function that somehow ``does the right thing,'' but relying solely on the representation type is insufficient to resolve intentional distinctions between conses, lists, alists, and trees, so we wouldn't know whether to call COPY-CONS (if there were such a thing), COPY-LIST, COPY-ALIST, or COPY-TREE.
Of course, we could just define that COPY-LIST is what's used, but that could (and probably would) give the misleading impression that such a choice was ``uniquely determined'' or even ``morally correct,'' instead of just arbitrarily chosen. Also, an arbitrary choice would leave users feeling funny about the presence of an operator that can copy many--but not all--kinds of objects.
* Courageous <jkras...@san.rr.com> | Never had that happen is all of 3 years using the code generator that I | had, which would, in fact, copy wildly crazy and cyclic graphs of objects | perfectly correctly.
that's a ridiculously bold statement. how do you _know_? would you even have _seen_ an error if it occurred?
| Furthermore, if you look into the world of object oriented databases, | you'll find that various oodbs also do this perfectly well.
they do it by restricting the domain to fit what they implement, but it fits the C++ mind-set to think this way and take it for granted that others will understand that "of _course_ we don't do the hard stuff". in this newsgroup and in Lisp, the trivial problems are uninspiring, and so we tend to think in broader terms. this means that arbitrary limits are regarded as cheating, unless yo present the limited domain up front as inherent engineering decisions. did you do that? no. foo on you for that.
| Perhaps we're having a definition of terms problem?
yes, you come from the C++ world. C++ people are extremely arrogant the way they think they invented object-orientedness. they didn't, OK? C++ doesn't even touch upon the really hard areas. you have to face this fact, or you'll make more ridiculously bold statements that reflect very badly on your ability to deal with complexity.
| I can't write a specific graph copier, because I don't have a- priori | knowledge of the structure of the objects that I will be copying.
at issue is knowing the _intent_ of the slots, not which they are. in Common Lisp we have the MOP to ask for the structure of the objects. piece of cake, really. just like databases can report on such things. it doesn't help one bit in deciding whether to make a shallow or deep copy, though.
* Courageous <jkras...@san.rr.com> | A tip for some of you folks: one of the keys to successful | communication is not to assume that the other person is wrong, | but rather to figure out what they're right *for*.
do you follow this tip yourself? if so, how come you don't listen? it seems to me you're telling us we're wrong because you have "seen" something you believe is sufficient when it isn't.
a tip for tippers: follow them yourself first, to _show_ how they work well for you. if not, just shut up about the tips.
Courageous wrote: > I have a CLOS object which may contain an arbitrary graph of other > objects. This graph may be cyclic. I'm looking for an easy way to deep > copy the object.
A generic deep copy function is impossible because in order to copy an object, you must know the meaning of the object and how it will be used.
``Why is there no generic COPY function?'' Common Lisp programmers often ask this question. [...]
Programmers want a generic COPY function that somehow ``does the right thing,'' but relying solely on the representation type is insufficient to resolve intentional distinctions between conses, lists, alists, and trees, so we wouldn't know whether to call COPY-CONS (if there were such a thing), COPY-LIST, COPY-ALIST, or COPY-TREE.
Of course, we could just define that COPY-LIST is what's used, but that could (and probably would) give the misleading impression that such a choice was ``uniquely determined'' or even ``morally correct,'' instead of just arbitrarily chosen. Also, an arbitrary choice would leave users feeling funny about the presence of an operator that can copy many--but not all--kinds of objects.
* Philip Lijnzaad <lijnz...@ebi.ac.uk> | On 09 Apr 2000 17:48:09 +0000, | "Erik" == Erik Naggum <e...@naggum.no> writes: | | Erik> implementing a mechanism that avoids descending into cyclic structures is | Erik> amazingly easy. | | if slightly non-obvious at first. For those confused, you have two parts of | your function step through the elements of a list, one going in single steps, | the other in steps of two. If they ever end up in the same element, the list | must have been circular. | | Erik> detection is easy with the rabbit and the hare algorithm. | | Is this an optimized version of the tortoise and the hare algorithm?
yes (my silly mistake). you just described it in the above paragraph, so you must know it under a different name, but the key is to realize that it only _detects_ a circularity, after it has happened -- there is no guarantee that you find the first such element.
Gareth Rees wrote: > A generic deep copy function is impossible because in order to copy an > object, you must know the meaning of the object and how it will be used.
I just remembered several years ago, quite fresh from spending a couple years `Eiffelling' I wanted a generic copy operation. I think I had even started writing the obvious trivial proposal for:
(defgeneric copy (context type source) (:documentation "Make a /type/ copy of /source/ according to /context/"))
and where all the standard predefined copy operations would be already hooked up, eg:
Philip Lijnzaad <lijnz...@ebi.ac.uk> writes: > On 09 Apr 2000 17:48:09 +0000, > "Erik" == Erik Naggum <e...@naggum.no> writes:
> Erik> implementing a mechanism that avoids descending into cyclic structures is > Erik> amazingly easy.
> if slightly non-obvious at first. For those confused, you have two parts of > your function step through the elements of a list, one going in single steps, > the other in steps of two. If they ever end up in the same element, the list > must have been circular.
No, hare&tortoise is nice, but it only does detection, which allows you to *stop* examining a circular graph, but doesn't allow you to immediately detect when you've completely traversed a graph that has one loop, nor to successfully traverse a graph with cross-loops.
To avoid descending more than once, you simply need to remember what you've already explored and not explore it again. Basically what a GC does.
> Erik> detection is easy with the rabbit and the hare algorithm.
> Is this an optimized version of the tortoise and the hare algorithm?
On 09 Apr 2000 20:32:37 +0100, Tim Bradshaw <t...@cley.com> wrote:
> Finally: I gave a paper on stuff related to this at last year's lugm, > I don't know if it's on the web anywhere, but franz's web site would > be a good place to look (I will put up a descendent of it at some > point, when I have some time).
Which paper do you refer to? I have the ELUGM '99 and LUGM '99 proceedings, but they don't include your papers. So I guess the Franz site doesn't have them either.
The ELUGM '99 proceedings' index refers to a talk by you titled "One Step Beyond" without the corresponding paper (but someone in this group said that you had problems attending the event). The LUGM '99 proceedings' index lists another talk without paper titled "Creeping features".
* Paolo Amoroso wrote: > The ELUGM '99 proceedings' index refers to a talk by you titled "One Step > Beyond" without the corresponding paper (but someone in this group said > that you had problems attending the event). The LUGM '99 proceedings' index > lists another talk without paper titled "Creeping features".
Creeping features & one step beyond are the same thing, and yes I didn't make it to elugm. I'm not sure why it's not in the lugm-99 proceedings, it should have been!