digitalmars.D - .clone()
- Florian RIVOAL (14/14) Jul 13 2004 hummm... I wonder if this is already available and i just didn't see wha...
- Jarrett Billingsley (14/14) Jul 13 2004 walter does not like shallow copies, and thus "deep" copies are built in...
- J Anderson (19/34) Jul 13 2004 I'm tempted to say - what the hell are you on about - but I won't. That...
- Jarrett Billingsley (3/4) Jul 13 2004 go ahead, say it :(
- J Anderson (7/22) Jul 13 2004 I brought this up before. I think it should be done by some standard
- Stewart Gordon (25/40) Jul 13 2004 For certain applications, such as duplicating a tree structure where
- pragma (19/25) Jul 13 2004 Here's my $0.02. :)
- Florian RIVOAL (2/28) Jul 13 2004
- Stewart Gordon (17/24) Jul 14 2004 Actually, we should be careful to distinguish between .dup (a shallow
- Florian RIVOAL (3/9) Jul 14 2004 Not sure either, but ask the guys at working for sun, they might have an...
- Stewart Gordon (19/32) Jul 14 2004 Java has the Cloneable interface. The default behaviour of objects that...
- pragma (27/57) Jul 14 2004 I see what you're saying by wanting to have mulitple ways to manipulate ...
- Stewart Gordon (16/35) Jul 14 2004 If arrays are anything to go by, then the 'standard' name already is
- pragma (11/28) Jul 14 2004 True enough. But now we really have two methods for the same basic oper...
- Stewart Gordon (22/30) Jul 14 2004 The point is that it enables a class to support two kinds of copy: deep
- pragma (11/29) Jul 14 2004 Stewart,
- Stewart Gordon (13/23) Jul 16 2004 Of course it can. Look at the class hierarchy to see if there are any
- Matthew Wilson (5/23) Jul 16 2004 anywhere? I
- Stewart Gordon (14/17) Jul 16 2004 Only if this feature is actually used in a given program.
- Florian RIVOAL (4/14) Jul 15 2004 I totaly agree with this way of doing things. I already can't wait to se...
- Regan Heath (75/93) Jul 14 2004 Isn't dup a deep copy currently?
- Andy Friesen (8/25) Jul 14 2004 erg. The problem is that subtracting behaviours from classes doesn't
- Matthew Wilson (7/7) Jul 16 2004 Guys
hummm... I wonder if this is already available and i just didn't see what's the correct syntax to do it, or if it is just plain missing. since all object are held by reference, there might be some times when you want to copy them. And if the object it self holds references to other object, in depth copy could be usefull. While there is no special difficulty to do it case by case, it seems to me there is no general way to do it. As i see it, the following stuff would be usefull: * a "Object clone()" method in the Object class. * a .clone property on arrays, behaving like this : - indentical to .dup for arrays of basic types - call .clone on all elements for array of array - call clone() on all elements for arrays of objects So is it there already under a different form? if not, is there a good reason why it is not there?
Jul 13 2004
walter does not like shallow copies, and thus "deep" copies are built into the language :) class A { int x; } void main() { A a=new A; A b=a; printf("%x\n%x\n",&a,&b); } this prints the addressess of a and b. clearly they are different, but b now has the same *data* as a. i love this feature :)
Jul 13 2004
Jarrett Billingsley wrote:walter does not like shallow copies, and thus "deep" copies are built into the language :) class A { int x; } void main() { A a=new A; A b=a; printf("%x\n%x\n",&a,&b); } this prints the addressess of a and b. clearly they are different, but b now has the same *data* as a. i love this feature :)I'm tempted to say - what the hell are you on about - but I won't. That is not a deep copy, that is a reference (or pointer) copy. D does not support shallow or deep copies for classes, although it does support shallow copies for structs. Take a gaze at this: class A { int x; } void main() { A a=new A; a.x = 10; A b=a; b.x = 11; printf("%d\n%d\n",a.x,b.x); //What is the result? } -- -Anderson: http://badmama.com.au/~anderson/
Jul 13 2004
I'm tempted to say - what the hell are you on aboutgo ahead, say it :( i forgot about that hidden level of abstraction in there. thus the pointers are different but each point to the same thing.
Jul 13 2004
Florian RIVOAL wrote:hummm... I wonder if this is already available and i just didn't see what's the correct syntax to do it, or if it is just plain missing. since all object are held by reference, there might be some times when you want to copy them. And if the object it self holds references to other object, in depth copy could be usefull. While there is no special difficulty to do it case by case, it seems to me there is no general way to do it. As i see it, the following stuff would be usefull: * a "Object clone()" method in the Object class. * a .clone property on arrays, behaving like this : - indentical to .dup for arrays of basic types - call .clone on all elements for array of array - call clone() on all elements for arrays of objects So is it there already under a different form? if not, is there a good reason why it is not there?I brought this up before. I think it should be done by some standard interfaces (or abstract classes -> which allow auto-up-casting). For automatic support, things would be easier when property RTTI comes in to D (fingers crossed for 2.0). -- -Anderson: http://badmama.com.au/~anderson/
Jul 13 2004
Florian RIVOAL wrote: <snip>since all object are held by reference, there might be some times when you want to copy them. And if the object it self holds references to other object, in depth copy could be usefull.For certain applications, such as duplicating a tree structure where there's a reason to do so. But I'm not sure that it's useful enough in general.While there is no special difficulty to do it case by case, it seems to me there is no general way to do it. As i see it, the following stuff would be usefull: * a "Object clone()" method in the Object class. * a .clone property on arrays, behaving like this : - indentical to .dup for arrays of basic types - call .clone on all elements for array of array - call clone() on all elements for arrays of objects So is it there already under a different form? if not, is there a good reason why it is not there?What would Object.clone do? (a) a memberwise shallow copy? (b) a memberwise deep copy, at the risk of running into a circular structure and taking up infinite time and memory? (c) just return this, enabling the class designer to override it to actually copy? If we chose (a) or (b), then class designers would need to override it if cloning the class makes no sense. This goes against the principle that subclasses are supposed to extend functionality, not remove it. This leaves (c), a safe default behaviour, which also enables arrays of objects to be cloned as deeply as allowed by the classes involved. But it also brings us back to the issue of the class designer forgetting to add new fields to the clone method, unless we give some kind of controlled access to built-in 'memberwise shallow copy' and 'memberwise clone' statements. Stewart. -- My e-mail is valid but not my primary mailbox, aside from its being the unfortunate victim of intensive mail-bombing at the moment. Please keep replies on the 'group where everyone may benefit.
Jul 13 2004
In article <cd168g$v4b$1 digitaldaemon.com>, Stewart Gordon says...What would Object.clone do? (a) a memberwise shallow copy? (b) a memberwise deep copy, at the risk of running into a circular structure and taking up infinite time and memory? (c) just return this, enabling the class designer to override it to actually copy?Here's my $0.02. :) There are plenty of reasons why you don't want to have a platform/language default to a deep copy for structs and classes. Circular structures (as you mentioned above) are just one example. Classes that aren't threadsafe, and protect fields via method access are another. You might not want to duplicate exceptionally deep trees and other huge data structures due to memory/cache issues. Arcane Jill has also mentioned in this NG the implications of not having explicit memory control when writing very-secure applications. Not being able to guard any member against a deep .clone() operation would be disasterous to such applications IMO. The best compromise would be something like (C): to have Object.clone default to a shallow copy, much like .dup() does for arrays. Actually, .dup() should probably be used for objects too for language consistency. If need be, clone()/.dup() can then be overridden to return anything the implementor wants: this, null, an uninitalized struct/class, or even throw an exception if the operation is not allowed. - Pragma
Jul 13 2004
In article <cd185b$12lr$1 digitaldaemon.com>, pragma <EricAnderton at yahoo dot com> says...In article <cd168g$v4b$1 digitaldaemon.com>, Stewart Gordon says...What would Object.clone do? (a) a memberwise shallow copy? (b) a memberwise deep copy, at the risk of running into a circular structure and taking up infinite time and memory? (c) just return this, enabling the class designer to override it to actually copy?Here's my $0.02. :) There are plenty of reasons why you don't want to have a platform/language default to a deep copy for structs and classes. Circular structures (as you mentioned above) are just one example. Classes that aren't threadsafe, and protect fields via method access are another. You might not want to duplicate exceptionally deep trees and other huge data structures due to memory/cache issues. Arcane Jill has also mentioned in this NG the implications of not having explicit memory control when writing very-secure applications. Not being able to guard any member against a deep .clone() operation would be disasterous to such applications IMO. The best compromise would be something like (C): to have Object.clone default to a shallow copy, much like .dup() does for arrays. Actually, .dup() should probably be used for objects too for language consistency. If need be, clone()/.dup() can then be overridden to return anything the implementor wants: this, null, an uninitalized struct/class, or even throw an exception if the operation is not allowed. - Pragma
Jul 13 2004
pragma <EricAnderton at yahoo dot com> wrote: <snip>The best compromise would be something like (C): to have Object.clone default to a shallow copy, much like .dup() does for arrays. Actually, .dup() should probably be used for objects too for language consistency.Actually, we should be careful to distinguish between .dup (a shallow copy, by definition) and .clone (a deep copy, or as deep as allowed). But yes, clone would _default_ to a shallow copy at the most. If we're going to have shallow object copies as a default behaviour, then we might as well define it in Object.dup, and have Object.clone simply call this method. We'd need clear documentation of the contractual difference between them.If need be, clone()/.dup() can then be overridden to return anything the implementor wants: this, null, an uninitalized struct/class, or even throw an exception if the operation is not allowed.I don't see any point in making this a disallowed operation. If copying the object doesn't make sense for whatever class, we might as well copy the reference instead. Stewart. -- My e-mail is valid but not my primary mailbox, aside from its being the unfortunate victim of intensive mail-bombing at the moment. Please keep replies on the 'group where everyone may benefit.
Jul 14 2004
Not sure either, but ask the guys at working for sun, they might have an idea, since java includes this behaviour : it throws an exception if you try to clone something without explicit mention it is clonable.If need be, clone()/.dup() can then be overridden to return anything the implementor wants: this, null, an uninitalized struct/class, or even throw an exception if the operation is not allowed.I don't see any point in making this a disallowed operation. If copying the object doesn't make sense for whatever class, we might as well copy the reference instead.
Jul 14 2004
Florian RIVOAL wrote:Java has the Cloneable interface. The default behaviour of objects that implement Cloneable is a shallow memberwise copy. Classes are allowed to override clone pretty much how they like. However, with Java data structures tending to accommodate arbitrary objects, you can't guarantee non-circularity. If you could, a typical behaviour would perhaps be to see if each element instanceof Cloneable, and if so then clone it, otherwise copy the reference. That's indeed what would happen with behaviour (c) I suggested. D is a different kettle of fish, as it has templates* and so little need to declare data structures taking arbitrary combinations of objects. But still, there is some caution to be taken. *OK, so Java has recently added them so I've heard, but plenty of legacy code is around using the Object structures.... Stewart. -- My e-mail is valid but not my primary mailbox, aside from its being the unfortunate victim of intensive mail-bombing at the moment. Please keep replies on the 'group where everyone may benefit.Not sure either, but ask the guys at working for sun, they might have an idea, since java includes this behaviour : it throws an exception if you try to clone something without explicit mention it is clonable.If need be, clone()/.dup() can then be overridden to return anything the implementor wants: this, null, an uninitalized struct/class, or even throw an exception if the operation is not allowed.I don't see any point in making this a disallowed operation. If copying the object doesn't make sense for whatever class, we might as well copy the reference instead.
Jul 14 2004
In article <cd2v1k$14m1$1 digitaldaemon.com>, Stewart Gordon says...pragma <EricAnderton at yahoo dot com> wrote: <snip>I see what you're saying by wanting to have mulitple ways to manipulate object cloning. It makes sense to have multiple methods for multiple needs (.dup vs clone). My question is: what would be the major differences/pros/cons between having dup and .clone defined side-by-side in Object instead of just having one or the other? If a class implementor were to just override the given method to give the behavior they want, then that's less trouble right?The best compromise would be something like (C): to have Object.clone default to a shallow copy, much like .dup() does for arrays. Actually, .dup() should probably be used for objects too for language consistency.Actually, we should be careful to distinguish between .dup (a shallow copy, by definition) and .clone (a deep copy, or as deep as allowed). But yes, clone would _default_ to a shallow copy at the most. If we're going to have shallow object copies as a default behaviour, then we might as well define it in Object.dup, and have Object.clone simply call this method. We'd need clear documentation of the contractual difference between them.class MySingleton{ static MySingleton _instance; static this(){ _instance = new MySingleton(); } // override one method to get the job done! void* opDup(){ // side-step duplication by returning the instance instead return _instance; } }('opDup' is contrived of course.. it could easily be 'dup' or something else) If there are fewer places where a given behavior exists, then it should be easier to maintain and hence less buggy. If the behavior of .dup and .clone are nearly identical, then maybe its better for bug control to make them the same method. IMO, if someone needs a more specialized kind of copying mechanism, then that should go in a specialised interface/class instead of on "Object"... especially if everyone else isn't going to need it.I agree that returing 'this' or a reference is probably the best default behavior possible. When I said "event throw an exception..." I was just thinking aloud with some examples of how overriding duplicate/clone freely would give a developer all the flexibility they needed. In my example above (MySingleton), I could just as easily put an 'assert' or 'throw' in there to prevent an *attempt* in duplication as to discourage its use completely... but that's just an example. I'm really glad this topic came along. C++ handles this kind of thing poorly and Java does a little better with use of ICloneable. Since duplication in D is built-into all the primitive types (arrays), why not make it a standard feature of objects and structs? Makes sense to me. - PragmaIf need be, clone()/.dup() can then be overridden to return anything the implementor wants: this, null, an uninitalized struct/class, or even throw an exception if the operation is not allowed.I don't see any point in making this a disallowed operation. If copying the object doesn't make sense for whatever class, we might as well copy the reference instead.
Jul 14 2004
pragma <EricAnderton at yahoo dot com> wrote: <snip>If arrays are anything to go by, then the 'standard' name already is dup, and it returns an object of the same type, not a void*.// override one method to get the job done! void* opDup(){ // side-step duplication by returning the instance instead return _instance; } }('opDup' is contrived of course.. it could easily be 'dup' or something else)If there are fewer places where a given behavior exists, then it should be easier to maintain and hence less buggy.I see little room for bugs in a function like Object clone() { return dup(); }If the behavior of .dup and .clone are nearly identical, then maybe its better for bug control to make them the same method.Any half-decent compiler would inline the above so that they become the same method. <snip><snip> this already is a reference. What other kind of reference would we return? Stewart. -- My e-mail is valid but not my primary mailbox, aside from its being the unfortunate victim of intensive mail-bombing at the moment. Please keep replies on the 'group where everyone may benefit.I don't see any point in making this a disallowed operation. If copying the object doesn't make sense for whatever class, we might as well copy the reference instead.I agree that returing 'this' or a reference is probably the best default behavior possible.
Jul 14 2004
In article <cd3jb9$26j4$1 digitaldaemon.com>, Stewart Gordon says...True enough. But now we really have two methods for the same basic operation by default. That doesn't seem useful to me. I suppose if clone were the only one of the two that is overridable that would be better, but then you still cannot stop clones from being generated through dup. One point of access solves more problems than two, IMO.If there are fewer places where a given behavior exists, then it should be easier to maintain and hence less buggy.I see little room for bugs in a function like Object clone() { return dup(); }Accept if the're overridable, in which case you're looking into the v-table and cannot optimize it away.If the behavior of .dup and .clone are nearly identical, then maybe its better for bug control to make them the same method.Any half-decent compiler would inline the above so that they become the same method.<snip>Oops, should've checked that one before hitting 'post'... you're right, it would be just plain 'ol 'this' by default as you suggested. :) - Pragma<snip> this already is a reference. What other kind of reference would we return?I don't see any point in making this a disallowed operation. If copying the object doesn't make sense for whatever class, we might as well copy the reference instead.I agree that returing 'this' or a reference is probably the best default behavior possible.
Jul 14 2004
pragma <EricAnderton at yahoo dot com> wrote: <snip>True enough. But now we really have two methods for the same basic operation by default. That doesn't seem useful to me. I suppose if clone were the only one of the two that is overridable that would be better, but then you still cannot stop clones from being generated through dup. One point of access solves more problems than two, IMO.The point is that it enables a class to support two kinds of copy: deep and shallow. The call to dup serves as a point at which the depth will stop, perhaps because that's where non-circularity can no longer be guaranteed, or because you reach a point at which no further depth can possibly exist. A class that serves as a data structure can override dup in order to create a copy of the whole structure, but not clone the individual elements. OTOH, it could override clone enabling the individual elements to be cloned as well. Giving the class user the choice is a possible generic programming advantage. <snip>AcceptPardon?if the're overridable, in which case you're looking into the v-table and cannot optimize it away.<snip> It can if the compiler determines that it isn't overridden anywhere below the type in which it is called. Stewart. -- My e-mail is valid but not my primary mailbox, aside from its being the unfortunate victim of intensive mail-bombing at the moment. Please keep replies on the 'group where everyone may benefit.
Jul 14 2004
In article <cd3oqo$2g0j$1 digitaldaemon.com>, Stewart Gordon says...The point is that it enables a class to support two kinds of copy: deep and shallow. The call to dup serves as a point at which the depth will stop, perhaps because that's where non-circularity can no longer be guaranteed, or because you reach a point at which no further depth can possibly exist. A class that serves as a data structure can override dup in order to create a copy of the whole structure, but not clone the individual elements. OTOH, it could override clone enabling the individual elements to be cloned as well. Giving the class user the choice is a possible generic programming advantage.Stewart, I'm glad you put it this way, as I think I understand now. And you're right: it does give a lot of flexibility when the differences between .dup and .clone are well-defined. I'm going to stick to my own opinion on this one, but I *do* see the merit in this design. They just don't feel orthogonal enough to me. Perhaps there's a compromise waiting for us down the road? :)<snip>::shrug:: I have no idea. Is it time for me to reinstall win2k already? ;)AcceptPardon?Do you mean if the compiler knows if a given class is subclassed anywhere? I don't think that's possible. - Pragmaif the're overridable, in which case you're looking into the v-table and cannot optimize it away.<snip> It can if the compiler determines that it isn't overridden anywhere below the type in which it is called.
Jul 14 2004
pragma <EricAnderton at yahoo dot com> wrote: <snip>Of course it can. Look at the class hierarchy to see if there are any subclasses, and then to see if any of them can ever be instantiated in the course of the program. This is one of the optimisation principles on which D was designed. See http://www.digitalmars.com/d/function.html However, it does require the modules to be compiled all in one go.... Stewart. -- My e-mail is valid but not my primary mailbox, aside from its being the unfortunate victim of intensive mail-bombing at the moment. Please keep replies on the 'group where everyone may benefit.Do you mean if the compiler knows if a given class is subclassed anywhere? I don't think that's possible.if the're overridable, in which case you're looking into the v-table and cannot optimize it away.<snip> It can if the compiler determines that it isn't overridden anywhere below the type in which it is called.
Jul 16 2004
"Stewart Gordon" <smjg_1998 yahoo.com> wrote in message news:cd89ef$383$1 digitaldaemon.com...pragma <EricAnderton at yahoo dot com> wrote: <snip>anywhere? IDo you mean if the compiler knows if a given class is subclassedif the're overridable, in which case you're looking into the v-table and cannot optimize it away.<snip> It can if the compiler determines that it isn't overridden anywhere below the type in which it is called.This precludes any dynamic loading of classes, then. I know that this is a 2.x feature, at best, but it's still probably good to consider.don't think that's possible.Of course it can. Look at the class hierarchy to see if there are any subclasses, and then to see if any of them can ever be instantiated in the course of the program. This is one of the optimisation principles on which D was designed. See http://www.digitalmars.com/d/function.html However, it does require the modules to be compiled all in one go....
Jul 16 2004
Matthew Wilson wrote: <snip>This precludes any dynamic loading of classes, then.Only if this feature is actually used in a given program.I know that this is a 2.x feature, at best, but it's still probably good to consider.I'd no idea that this was planned. But I can't see it working well with a compiled OO model, perhaps unless some superclass is specified at compile time, and access to the dynamically loaded class from outside is restricted to members of that superclass. In that case, only those methods of those superclasses need be made virtual for the sake of dynamic loading. Stewart. -- My e-mail is valid but not my primary mailbox, aside from its being the unfortunate victim of intensive mail-bombing at the moment. Please keep replies on the 'group where everyone may benefit.
Jul 16 2004
The point is that it enables a class to support two kinds of copy: deep and shallow. The call to dup serves as a point at which the depth will stop, perhaps because that's where non-circularity can no longer be guaranteed, or because you reach a point at which no further depth can possibly exist. A class that serves as a data structure can override dup in order to create a copy of the whole structure, but not clone the individual elements. OTOH, it could override clone enabling the individual elements to be cloned as well. Giving the class user the choice is a possible generic programming advantage.I totaly agree with this way of doing things. I already can't wait to see it implemented. But i am skipping some steps. Before thinking of seeing added to D, it seems quite reasonable to ask Walter what he thinks about this matter. So, Walter, any opinion on .dup, .clone and the like?
Jul 15 2004
On Wed, 14 Jul 2004 10:40:03 +0100, Stewart Gordon <smjg_1998 yahoo.com> wrote:pragma <EricAnderton at yahoo dot com> wrote: <snip>Isn't dup a deep copy currently? eg. char[] p = "regan"; char[] s; s = p //shallow copy p and s refer to same data s = p.dup //deep copy, p and s refer to different data assuming this, then when you write a struct or class you can simply define a 'dup' member and get both shallow and deep copying, right? import std.c.stdlib; extern (C) { void *memcpy( void *dest, void *src, size_t count); } struct A { void* pdata = null; uint plen = 0; void set(char[] _data) { *this = A.init; if (!(_data is null)) { pdata = cast(void*)_data; plen = _data.length; } } A dup() { A a; a.pdata = malloc(plen); memcpy(a.pdata,pdata,plen); return a; } } class B { A a; this(char[] _a = null) { a.set(_a); } B dup() { B b = new B(); b.a = a.dup; return b; } } void main() { char[] data = "regan"; A aa; A ab; B ba = new B(data); B bb = new B(); aa.set(data); ab = aa; //shallow copy printf("A %08x %08x\n",aa.pdata,ab.pdata); ab = aa.dup; //deep copy printf("A %08x %08x\n",aa.pdata,ab.pdata); bb = ba; //shallow copy printf("B %08x %08x\n",ba.a.pdata,bb.a.pdata); bb = ba.dup; //deep copy printf("B %08x %08x\n",ba.a.pdata,bb.a.pdata); } Object could have a default dup' method that asserted or threw an exception. Basic types i.e. int could have a dup method added to them that simply returned themselves. Thus for any object you could call the dup method to do a deep copy, and for any object you could do a shallow copy simply by assigning it. Regan. -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/The best compromise would be something like (C): to have Object.clone default to a shallow copy, much like .dup() does for arrays. Actually, .dup() should probably be used for objects too for language consistency.Actually, we should be careful to distinguish between .dup (a shallow copy, by definition) and .clone (a deep copy, or as deep as allowed). But yes, clone would _default_ to a shallow copy at the most. If we're going to have shallow object copies as a default behaviour, then we might as well define it in Object.dup, and have Object.clone simply call this method. We'd need clear documentation of the contractual difference between them.If need be, clone()/.dup() can then be overridden to return anything the implementor wants: this, null, an uninitalized struct/class, or even throw an exception if the operation is not allowed.I don't see any point in making this a disallowed operation. If copying the object doesn't make sense for whatever class, we might as well copy the reference instead.
Jul 14 2004
Florian RIVOAL wrote:hummm... I wonder if this is already available and i just didn't see what's the correct syntax to do it, or if it is just plain missing. since all object are held by reference, there might be some times when you want to copy them. And if the object it self holds references to other object, in depth copy could be usefull. While there is no special difficulty to do it case by case, it seems to me there is no general way to do it. As i see it, the following stuff would be usefull: * a "Object clone()" method in the Object class. * a .clone property on arrays, behaving like this : - indentical to .dup for arrays of basic types - call .clone on all elements for array of array - call clone() on all elements for arrays of objects So is it there already under a different form? if not, is there a good reason why it is not there?erg. The problem is that subtracting behaviours from classes doesn't work. The best you can get is to do what Java does and throw an exception when some "un-inherited" method is called. Second, loading more stuff into the Object class means loading more baggage into every object created anywhere, even if all it ever does is throw an AssertionFailure. -- andy
Jul 14 2004
Guys I confess I've not followed much of this thread, but I've just had cause to make a clone() method, in the ranges in DTL. Its semantic is that a range instance is created that has the same enumeration state as its receiver. Whether this is deep or shallow is dependent on the specifics of the range class - it's the enumeration state that's important. Does this tally with your opinions?
Jul 16 2004