digitalmars.D - Round-up of the excuses for keeping Object.opCmp, and rebuttals to
- Stewart Gordon (49/49) Sep 13 2004 1. "It's needed for associative arrays to work."
- Ivan Senji (4/53) Sep 13 2004 A very nice post! And a great illustration of problems that opCmp causes
- Ben Hinkle (13/62) Sep 13 2004 "excuses"? that's a pretty heavy word to use.
-
Stewart Gordon
(8/14)
Sep 14 2004
- Matthew (133/174) Mar 08 2005 First of all, I have to say that I think the whole idea of opCmp, and
- Andrew Fedoniouk (96/277) Mar 08 2005 Matthew, may I translate your proposal(?)(below) into this:
- Ben Hinkle (7/17) Mar 09 2005 I could see having the generic array.sort use TypeInfo and its opCmp and...
- Stewart Gordon (26/36) Mar 09 2005 By a direct translation of the DMD algorithm, or by coding up a better
- Andrew Fedoniouk (6/10) Mar 09 2005 My point is simple.
- Ben Hinkle (19/30) Mar 09 2005 I just pasted your template into std.array and recompiled phobos. I took...
- Andrew Fedoniouk (7/11) Mar 09 2005 Yep, exactly. This is the case when templates work.
- Ben Hinkle (13/34) Mar 09 2005 I don't have any problem with that, either. One advantage I see of the
- Stewart Gordon (10/29) Mar 09 2005 What would be the purpose of making address of instance the default
- Matthew (5/30) Mar 09 2005 For AAs, not for arrays. Sorting an array of X would be a compiler
-
Stewart Gordon
(16/25)
Mar 09 2005
1. "It's needed for associative arrays to work." Firstly, this is implementation-specific. There's no need for AAs to be implemented in terms of Object.opCmp, or to rely on an ordering comparator at all. True, an ordering can make AAs more efficient, but only where one exists. If anything, then in the current state Object.opCmp is _preventing_ AAs from working on classes that have no ordering. 2. "How else can an array of objects be sorted?" Objects of diverse classes aren't going to be mutually comparable. Of course, one might want to design two or more classes with comparability between them. But if that's the case, then they would derive from some common base class or interface that defines the scope of mutual comparability. Then sorting an Object[] doesn't make sense, but sorting an array of the common base class does. 3. "Putting it in Object reserves a predictable vtbl[] slot for it." If everything (e.g. array aggregate properties when/if we get them) followed the implementation pattern of AAs and sort, then we would end up with a lot of vtbl slots reserved for operators that tend to do nothing, and even more coding errors to slip through the compiler to the runtime. Even if there's some reason to keep AAs and sort to use this mechanism, rather than reimplementing them by templates, then reserving a predictable vtbl slot doesn't need to be done by actually filling it. It could be done simply by specifying a slot to be reserved in the ABI. In Object, this slot would be set to null, and the compiler would fill it only when a class Qwert defines an opCmp(Qwert). Of course, if you then have something like class Yuiop : Qwert { int opCmp(Qwert q) { ... } int opCmp(Yuiop y) { ... } } then the first form would override. This makes sense, as it only makes sense for Yuiop.opCmp(Qwert) to call Yuiop.opCmp(Yuiop) if the argument is a Yuiop. Then we have a principle: the reserved slot would carry the form of opCmp that reflects the scope of mutual comparability. Hence Yuiop[].sort and Qwert[].sort would behave identically, and consistently with uses of the comparison operators directly (under current rules) regardless of which types are declared. Then, when an AA is declared of a certain class as keytype, the compiler would look in that class's vtbl to see if the opCmp slot is set to null; in which case, it would hook up an AA implementation that doesn't rely on opCmp. Similarly, when sort is called on an array, the compiler would report an error if opCmp is null. Maybe some other excuses have been brought up before, but I can't think of them. Is there anything to add to this list? Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Sep 13 2004
"Stewart Gordon" <smjg_1998 yahoo.com> wrote in message news:ci46lq$19k0$1 digitaldaemon.com...1. "It's needed for associative arrays to work." Firstly, this is implementation-specific. There's no need for AAs to be implemented in terms of Object.opCmp, or to rely on an ordering comparator at all. True, an ordering can make AAs more efficient, but only where one exists. If anything, then in the current state Object.opCmp is _preventing_ AAs from working on classes that have no ordering. 2. "How else can an array of objects be sorted?" Objects of diverse classes aren't going to be mutually comparable. Of course, one might want to design two or more classes with comparability between them. But if that's the case, then they would derive from some common base class or interface that defines the scope of mutual comparability. Then sorting an Object[] doesn't make sense, but sorting an array of the common base class does. 3. "Putting it in Object reserves a predictable vtbl[] slot for it." If everything (e.g. array aggregate properties when/if we get them) followed the implementation pattern of AAs and sort, then we would end up with a lot of vtbl slots reserved for operators that tend to do nothing, and even more coding errors to slip through the compiler to the runtime. Even if there's some reason to keep AAs and sort to use this mechanism, rather than reimplementing them by templates, then reserving a predictable vtbl slot doesn't need to be done by actually filling it. It could be done simply by specifying a slot to be reserved in the ABI. In Object, this slot would be set to null, and the compiler would fill it only when a class Qwert defines an opCmp(Qwert). Of course, if you then have something like class Yuiop : Qwert { int opCmp(Qwert q) { ... } int opCmp(Yuiop y) { ... } } then the first form would override. This makes sense, as it only makes sense for Yuiop.opCmp(Qwert) to call Yuiop.opCmp(Yuiop) if the argument is a Yuiop. Then we have a principle: the reserved slot would carry the form of opCmp that reflects the scope of mutual comparability. Hence Yuiop[].sort and Qwert[].sort would behave identically, and consistently with uses of the comparison operators directly (under current rules) regardless of which types are declared. Then, when an AA is declared of a certain class as keytype, the compiler would look in that class's vtbl to see if the opCmp slot is set to null; in which case, it would hook up an AA implementation that doesn't rely on opCmp. Similarly, when sort is called on an array, the compiler would report an error if opCmp is null. Maybe some other excuses have been brought up before, but I can't think of them. Is there anything to add to this list?A very nice post! And a great illustration of problems that opCmp causes :)Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Sep 13 2004
"excuses"? that's a pretty heavy word to use. More to the point, Walter knows the TypeInfo system needs more bake-time. opCmp is crucial to Object's TypeInfo. So any proposal to remove opCmp needs to include a proposal to modify the TypeInfo for Object or to modify the TypeInfo system generally. I don't know the details about how the compiler manages TypeInfos (I know there are a bunch in std/typeinfo but I don't know if the compiler looks at that code during compiling or only at link time). Since the TypeInfo existed before templates I wouldn't be surprised if some parts of TypeInfo can be removed - but it would probably take non-trivial compiler changes. I don't know if Walter is going to take the time to step back and look at TypeInfos before 1.0. "Stewart Gordon" <smjg_1998 yahoo.com> wrote in message news:ci46lq$19k0$1 digitaldaemon.com...1. "It's needed for associative arrays to work." Firstly, this is implementation-specific. There's no need for AAs to be implemented in terms of Object.opCmp, or to rely on an ordering comparator at all. True, an ordering can make AAs more efficient, but only where one exists. If anything, then in the current state Object.opCmp is _preventing_ AAs from working on classes that have no ordering. 2. "How else can an array of objects be sorted?" Objects of diverse classes aren't going to be mutually comparable. Of course, one might want to design two or more classes with comparability between them. But if that's the case, then they would derive from some common base class or interface that defines the scope of mutual comparability. Then sorting an Object[] doesn't make sense, but sorting an array of the common base class does. 3. "Putting it in Object reserves a predictable vtbl[] slot for it." If everything (e.g. array aggregate properties when/if we get them) followed the implementation pattern of AAs and sort, then we would end up with a lot of vtbl slots reserved for operators that tend to do nothing, and even more coding errors to slip through the compiler to the runtime. Even if there's some reason to keep AAs and sort to use this mechanism, rather than reimplementing them by templates, then reserving a predictable vtbl slot doesn't need to be done by actually filling it. It could be done simply by specifying a slot to be reserved in the ABI. In Object, this slot would be set to null, and the compiler would fill it only when a class Qwert defines an opCmp(Qwert). Of course, if you then have something like class Yuiop : Qwert { int opCmp(Qwert q) { ... } int opCmp(Yuiop y) { ... } } then the first form would override. This makes sense, as it only makes sense for Yuiop.opCmp(Qwert) to call Yuiop.opCmp(Yuiop) if the argument is a Yuiop. Then we have a principle: the reserved slot would carry the form of opCmp that reflects the scope of mutual comparability. Hence Yuiop[].sort and Qwert[].sort would behave identically, and consistently with uses of the comparison operators directly (under current rules) regardless of which types are declared. Then, when an AA is declared of a certain class as keytype, the compiler would look in that class's vtbl to see if the opCmp slot is set to null; in which case, it would hook up an AA implementation that doesn't rely on opCmp. Similarly, when sort is called on an array, the compiler would report an error if opCmp is null. Maybe some other excuses have been brought up before, but I can't think of them. Is there anything to add to this list? Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Sep 13 2004
Ben Hinkle wrote:"excuses"? that's a pretty heavy word to use. More to the point, Walter knows the TypeInfo system needs more bake-time. opCmp is crucial to Object's TypeInfo. So any proposal to remove opCmp needs to include a proposal to modify the TypeInfo for Object or to modify the TypeInfo system generally.<snip top of upside-down reply> LTIK, the plan was to have class-specific TypeInfos - this is already a shortcoming in a handful of respects. But yes, you have a point. Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Sep 14 2004
First of all, I have to say that I think the whole idea of opCmp, and any similar things involving/requiring Object references and casts, stinks. I don't have to do any of this kind of crap in C++ to achieve all manner of efficient and generic manipulation. One has to to it in Java/.NET, but then .... <editor: censored for being rude and repetitive> ... which D claims to be an evolution from. Specific responses (and the occasional gripe) within. "Stewart Gordon" <smjg_1998 yahoo.com> wrote in message news:ci46lq$19k0$1 digitaldaemon.com...1. "It's needed for associative arrays to work." Firstly, this is implementation-specific. There's no need for AAs to be implemented in terms of Object.opCmp, or to rely on an ordering comparator at all. True, an ordering can make AAs more efficient, but only where one exists. If anything, then in the current state Object.opCmp is _preventing_ AAs from working on classes that have no ordering.Since AAs are built-in, why on earth do they need to rely on runtime polymorphism. Given the classes X and Y class X { } class Y { int opCmp(Y rhs); } What prevents the compiler from generating sorting based on arbitrary difference (e.g. address of instance) for X, and using the *non-polymorphic* Y::opCmp(Y) ?? (I know all methods are polymorphic in D, but2. "How else can an array of objects be sorted?"Why should an array of arbitrary types be sortable?? For example, suppose I have an array of PGP keys, or MD5 hashes. In what way could one define a meaningful ordering relation? (Naturally one can go lexicographical, but that's an arbitrary choice. Might as well order on which one contains the longest character sub-sequence of the word "Churlish" within them.) If anyone truly wants to be able to meaningfully sort a Chair, a Weather, an Md5Sum, and a Vector object held in an array, then they need to strap on the latex and go wibble! Sorting of Object is utterly bogus, and should be immediately dispensed with. (However, polymorphic sorting is not necessarily wrong .... )Objects of diverse classes aren't going to be mutually comparable.Abso-stinkingly-lutely not!! It's really easy. For polymorphic classes, such as the following, one can easily support comparison: class Base { int opCmp(Base); // NOTE: param is Base } class Derived : public Base { } class MoreDerived : public Derived { } As it stands above, a heterogeneous array of instances of these types, say [b1, b2, d1, md1, b3, md2, b4] would be sortable because Base derives opCmp(). Moreover, it would be sorted solely by the implementation of Base.opCmp() for all instances irrespective of their actual type. Base[] ba = [b1, b2, d1, md1, b3, md2, b4]; ba.sort; // Calls Base.opCmp(Base) Similarly, an array of Derived (and MoreDerived) would also sort using Base.opCmp(). Derived[] da = [d1, md1, md2] da.sort; // Calls Base.opCmp(Base) Now consider that the author of MoreDerived knows - and being the author of the class, he/she's the one that will know - that they need to override the sorting logic for sorting of non-heterogeneous arrays of MoreDerived instances. (Actually, it's not "non-heterogeneous arrays of MoreDerived instances." In fact, it's "Arrays of MoreDerived (and any class derived from MoreDerived).") All he/she does is define an _overload_, opCmp(MoreDerived) class MoreDerived : public Derived { int opCmp(MoreDerived); // Note: param is MoreDerived } MoreDerived mda = [md1, md2] mda.sort; // Calls MoreDerived.opCmp(MoreDerived) Now, when the compiler is generating the code to sort an array on MoreDerived, it will implement it in terms of MoreDerived.opCmp(MoreDerived). It will do _nothing_ with Base.opCmp(). Indeed, if the author of Base was to remove Base.opCmp(Base), rendering the lines "ba.sort;" and "da.sort;" as compile errors, the line "mda.sort;" would be entirely unaffected and would remain legal. Now consider that the author of Derived wants to override opCmp(Base), in order to something a little smarter when involved in Base sorting. Now "ba.sort;" will use both Base.opCmp(Base) and Derived.opCmp(Base). That's perfectly right, because MoreDerived, as a child of Derived that has _not_ overrident opCmp(Base), is implicitly defined to be happy with what Derived does when Base sorting. (Child-Base coupling fragility necessarily applies here, of course, but that's an artefact of OO, and nothing to do with the ramifications of this proposal.) The important thing is that this amendment to the hierarchy does not affect "mda.sort;" one whit. It still uses MoreDerived.sort(MoreDerived). In practical terms, the issues of what-actually-happens-in-the-sort and comparing-with-same-type-or-base-type will remain as they do in current OO hierarchies. The important distinction between this and the current situation is that the compiler sorts using the deepest opCmp() available for all types in the array. (I've not considered what other sorting arrangements we might consider, but I suspect that polymorphism will resolve to the deepest one in sorting template containers, doing what I propose for the compiler do as a natural part of runtime polymorphism. Which is nice. <g>) Since a corollary stipulation is that comparison will only be allowed (_at_compile_time_ !! Hurrah!!!) for types for which it is sensible to do so - i.e. those that are polymorphically related from a common non-root subtree of the class hierarchy and have, in a common ancestor, an opCmp defined - we'd totally avoid all that evil is-rhs-same-type-if-not-throw guff. In a sense, this would be like the IComparable stuff, but for the fact that any shared parent that sports an opCmp can act as such an 'interface', without the attendant hassles of a prescribed interface. I respectfully submit that this proposal completely obviates the need for opCmp as far as arrays are concerned and, in addition, affords us the opportunity to tighted up type resolution at compile time. All without costing us any flexibility. (There may, of course, be unforeseen drawbacks, as I'm less omniscient than I was ...)3. "Putting it in Object reserves a predictable vtbl[] slot for it." If everything (e.g. array aggregate properties when/if we get them) followed the implementation pattern of AAs and sort, then we would end up with a lot of vtbl slots reserved for operators that tend to do nothing, and even more coding errors to slip through the compiler to the runtime.Agreed. Why not have a vtbl[] slot for everthing: serialisation, sorting, stringising, etc. etc. etc. In a modern language with powerful generic facilities - templates, shims (if we ever devise the way to do these in D), interfaces, mixins, etc. - why do we need to put on such straight jackets? (That question's not entirely rhetorical. If there is a genuine reason that benefits the user, and not just the implementor, then I'm very interested to hear it.)Even if there's some reason to keep AAs and sort to use this mechanism, rather than reimplementing them by templates, then reserving a predictable vtbl slot doesn't need to be done by actually filling it. It could be done simply by specifying a slot to be reserved in the ABI. In Object, this slot would be set to null, and the compiler would fill it only when a class Qwert defines an opCmp(Qwert).True, but that's now something that sucks 50% rather than 100%. Let's get 0.Of course, if you then have something like class Yuiop : Qwert { int opCmp(Qwert q) { ... } int opCmp(Yuiop y) { ... } } then the first form would override. This makes sense, as it only makes sense for Yuiop.opCmp(Qwert) to call Yuiop.opCmp(Yuiop) if the argument is a Yuiop. Then we have a principle: the reserved slot would carry the form of opCmp that reflects the scope of mutual comparability. Hence Yuiop[].sort and Qwert[].sort would behave identically, and consistently with uses of the comparison operators directly (under current rules) regardless of which types are declared. Then, when an AA is declared of a certain class as keytype, the compiler would look in that class's vtbl to see if the opCmp slot is set to null; in which case, it would hook up an AA implementation that doesn't rely on opCmp. Similarly, when sort is called on an array, the compiler would report an error if opCmp is null.Hmmm. This seems somewhat similar to my proposal above, apart from the slot business (which I say has no business being a concern) - maybe I should have read all your post before starting my reply. <CG> Seriously, though, the fact that two people have come up with a similar idea for kicking opCmp() out of where it has no business being gives some encouragement, methinks.Maybe some other excuses have been brought up before, but I can't think of them. Is there anything to add to this list?Since your post elicited little response in September, we must surmise that there are none. Walter, I commend this idea for your sincerest consideration. :-) Matthew
Mar 08 2005
Matthew, may I translate your proposal(?)(below) into this: "opCmp based array sort is bad. It does not support runtime polymorphism. We need to change this" Am I right? I've attached simple template (with example) template sort(T) { void sort(T[] arr, int function(in T l, in T r) cmp) {...} } used as: int arr[] = new int[10000]; sort!(int) ( arr, function int(int i1,int i2) { return i1 - i2; } ); It is simple and works *30% faster* than D builtin sort. Walter, please, pay attention - C standard qsort is a piece of... Andrew Fedoniouk. http://terrainformatica.com "Matthew" <admin stlsoft.dot.dot.dot.dot.org> wrote in message news:d0lsv5$1496$1 digitaldaemon.com...First of all, I have to say that I think the whole idea of opCmp, and any similar things involving/requiring Object references and casts, stinks. I don't have to do any of this kind of crap in C++ to achieve all manner of efficient and generic manipulation. One has to to it in Java/.NET, but then .... <editor: censored for being rude and repetitive> ... which D claims to be an evolution from. Specific responses (and the occasional gripe) within. "Stewart Gordon" <smjg_1998 yahoo.com> wrote in message news:ci46lq$19k0$1 digitaldaemon.com...begin 666 sorter.d M;G0 <F%N9&]M*"D-"GL-"B <F5T=7)N(" H<F%N9&]M4V5E9" ](')A;F1O M871E('-O<G0H5"D-"GL-"B =F]I9"!S;W)T*%1;72!A<G(L(&EN="!F=6YC M;&5N9W1H(#P M<R :6YO=70 5"!T,2P :6YO=70 5"!T,B I( T*(" (" >R -"B (" M('0R(#T =#L ?0T*(" (" ?0T*(" (" =F]I9"!S=V%P*"!I;F]U="!4 M86-K6S P73L-"B (" (&EN="H =&]P(#T <W1A8VL[( T*(" (" :6YT M(&QI;6ET("T 8F%S93L-" T*(" (" (" (&EN="!I.PT*(" (" (" M(&EN="!J.PT*(" (" (" (&EN="!P:79O=#L-" T*(" (" (" (&EF M*&QE;B ^('%U:6-K7W-O<G1?=&AR97-H;VQD*0T*(" (" (" ('L-"B M(" (" (" (" +R\ =V4 =7-E(&)A<V4 *R!L96XO,B!A<R!T:&4 <&EV M;W0-"B (" (" (" (" <&EV;W0 /2!B87-E("L ;&5N("\ ,CL-"B M(" (" (" (" (&D /2!B87-E("L ,3L-"B (" (" (" (" :B ] M(&QI;6ET("T ,3L-" T*(" (" (" (" (" O+R!N;W< 96YS=7)E('1H M870 *FD /#T *F)A<V4 /#T M;&5S<RAA<G);8F%S95TL87)R6VE=*3L-"B (" (" (" (" <W=A<%]I M<B [.RD-"B (" (" (" (" >PT*(" (" (" (" (" (" 9&\ M:2LK.R!W:&EL92 87)R6VE=(#P (&%R<EMB87-E72 I.PT*(" (" (" M(" (" (" 9&\ :BTM.R!W:&EL92 87)R6V)A<V5=(#P (&%R<EMJ72 I M.PT*(" (" (" (" (" (" :68H(&D /B!J("D-"B (" (" (" M(" (" ('L-"B (" (" (" (" (" (" ("!B<F5A:SL-"B (" M(" (" (" (" ('T-"B (" (" (" (" (" ('-W87 H87)R6VE= M;F]W+"!P=7-H('1H92!L87)G97-T('-U8BUA<G)A>0T*(" (" (" (" M("!I9BAJ("T 8F%S92 ^(&QI;6ET("T :2D-"B (" (" (" (" >PT* M(" (" (" (" (" (" =&]P6S!=(#T 8F%S93L-"B (" (" (" M(" (" (" (" >PT*(" (" (" (" (" (" =&]P6S!=(#T :3L- M"B (" (" (" (" (" ('1O<%LQ72 ](&QI;6ET.PT*(" (" (" M(" (" (" ;&EM:70 (#T :CL-"B (" (" (" (" ?0T*(" (" M90T*(" (" (" ('L-"B (" (" (" (" +R\ =&AE('-U8BUA<G)A M>2!I<R!S;6%L;"P <&5R9F]R;2!I;G-E<G1I;VX <V]R= T*(" (" (" M(" ("!J(#T M(" (" (" (" (&9O<B [(&D /"!L:6UI=#L :B ](&DL(&DK*RD-"B M(" (" (" (" >PT*(" (" (" (" (" (" 9F]R*#L 8VUP*&%R M('L-"B (" (" (" (" (" (" ("!S=V%P*&%R<EMJ("L ,5TL87)R M6VI=*3L-"B (" (" (" (" (" (" ("!I9BAJ(#T M(" (" (" (" (" (" (" (" (&)R96%K.PT*(" (" (" (" M73L-"B (" (" (" (" (" (&QI;6ET(#T =&]P6S%=.PT*(" (" M;G0 8VUP:2AI;B!I;G0 :3$L:6X :6YT(&DR*2![(')E='5R;B!I,2 M(&DR M:"AI;F]U="!I;G0 93L 87)R*0T*"0EE(#T M(&DQ+&EN="!I,BD >R!R971U<FX :3$ +2!I,CL ?2 I.PT*"0DO+V%R<BYS M<T-O=6YT97(N:6YT97)V86Q?='EP92 ;7,Q(#T 8V]U;G1E<BYM:6QL:7-E M8V]N9',H*3L-" T*"7=R:71E9B B=&EM92 ]("5D;7-<;B(L(&US,2 I.PT* M"0T*"7=R:71E9B B=" ]("5D7&XB+"!A<G);,%T *3L-" EW<FET968H(G0 ` end1. "It's needed for associative arrays to work." Firstly, this is implementation-specific. There's no need for AAs to be implemented in terms of Object.opCmp, or to rely on an ordering comparator at all. True, an ordering can make AAs more efficient, but only where one exists. If anything, then in the current state Object.opCmp is _preventing_ AAs from working on classes that have no ordering.Since AAs are built-in, why on earth do they need to rely on runtime polymorphism. Given the classes X and Y class X { } class Y { int opCmp(Y rhs); } What prevents the compiler from generating sorting based on arbitrary difference (e.g. address of instance) for X, and using the *non-polymorphic* Y::opCmp(Y) ?? (I know all methods are polymorphic in D, but2. "How else can an array of objects be sorted?"Why should an array of arbitrary types be sortable?? For example, suppose I have an array of PGP keys, or MD5 hashes. In what way could one define a meaningful ordering relation? (Naturally one can go lexicographical, but that's an arbitrary choice. Might as well order on which one contains the longest character sub-sequence of the word "Churlish" within them.) If anyone truly wants to be able to meaningfully sort a Chair, a Weather, an Md5Sum, and a Vector object held in an array, then they need to strap on the latex and go wibble! Sorting of Object is utterly bogus, and should be immediately dispensed with. (However, polymorphic sorting is not necessarily wrong .... )Objects of diverse classes aren't going to be mutually comparable.Abso-stinkingly-lutely not!! It's really easy. For polymorphic classes, such as the following, one can easily support comparison: class Base { int opCmp(Base); // NOTE: param is Base } class Derived : public Base { } class MoreDerived : public Derived { } As it stands above, a heterogeneous array of instances of these types, say [b1, b2, d1, md1, b3, md2, b4] would be sortable because Base derives opCmp(). Moreover, it would be sorted solely by the implementation of Base.opCmp() for all instances irrespective of their actual type. Base[] ba = [b1, b2, d1, md1, b3, md2, b4]; ba.sort; // Calls Base.opCmp(Base) Similarly, an array of Derived (and MoreDerived) would also sort using Base.opCmp(). Derived[] da = [d1, md1, md2] da.sort; // Calls Base.opCmp(Base) Now consider that the author of MoreDerived knows - and being the author of the class, he/she's the one that will know - that they need to override the sorting logic for sorting of non-heterogeneous arrays of MoreDerived instances. (Actually, it's not "non-heterogeneous arrays of MoreDerived instances." In fact, it's "Arrays of MoreDerived (and any class derived from MoreDerived).") All he/she does is define an _overload_, opCmp(MoreDerived) class MoreDerived : public Derived { int opCmp(MoreDerived); // Note: param is MoreDerived } MoreDerived mda = [md1, md2] mda.sort; // Calls MoreDerived.opCmp(MoreDerived) Now, when the compiler is generating the code to sort an array on MoreDerived, it will implement it in terms of MoreDerived.opCmp(MoreDerived). It will do _nothing_ with Base.opCmp(). Indeed, if the author of Base was to remove Base.opCmp(Base), rendering the lines "ba.sort;" and "da.sort;" as compile errors, the line "mda.sort;" would be entirely unaffected and would remain legal. Now consider that the author of Derived wants to override opCmp(Base), in order to something a little smarter when involved in Base sorting. Now "ba.sort;" will use both Base.opCmp(Base) and Derived.opCmp(Base). That's perfectly right, because MoreDerived, as a child of Derived that has _not_ overrident opCmp(Base), is implicitly defined to be happy with what Derived does when Base sorting. (Child-Base coupling fragility necessarily applies here, of course, but that's an artefact of OO, and nothing to do with the ramifications of this proposal.) The important thing is that this amendment to the hierarchy does not affect "mda.sort;" one whit. It still uses MoreDerived.sort(MoreDerived). In practical terms, the issues of what-actually-happens-in-the-sort and comparing-with-same-type-or-base-type will remain as they do in current OO hierarchies. The important distinction between this and the current situation is that the compiler sorts using the deepest opCmp() available for all types in the array. (I've not considered what other sorting arrangements we might consider, but I suspect that polymorphism will resolve to the deepest one in sorting template containers, doing what I propose for the compiler do as a natural part of runtime polymorphism. Which is nice. <g>) Since a corollary stipulation is that comparison will only be allowed (_at_compile_time_ !! Hurrah!!!) for types for which it is sensible to do so - i.e. those that are polymorphically related from a common non-root subtree of the class hierarchy and have, in a common ancestor, an opCmp defined - we'd totally avoid all that evil is-rhs-same-type-if-not-throw guff. In a sense, this would be like the IComparable stuff, but for the fact that any shared parent that sports an opCmp can act as such an 'interface', without the attendant hassles of a prescribed interface. I respectfully submit that this proposal completely obviates the need for opCmp as far as arrays are concerned and, in addition, affords us the opportunity to tighted up type resolution at compile time. All without costing us any flexibility. (There may, of course, be unforeseen drawbacks, as I'm less omniscient than I was ...)3. "Putting it in Object reserves a predictable vtbl[] slot for it." If everything (e.g. array aggregate properties when/if we get them) followed the implementation pattern of AAs and sort, then we would end up with a lot of vtbl slots reserved for operators that tend to do nothing, and even more coding errors to slip through the compiler to the runtime.Agreed. Why not have a vtbl[] slot for everthing: serialisation, sorting, stringising, etc. etc. etc. In a modern language with powerful generic facilities - templates, shims (if we ever devise the way to do these in D), interfaces, mixins, etc. - why do we need to put on such straight jackets? (That question's not entirely rhetorical. If there is a genuine reason that benefits the user, and not just the implementor, then I'm very interested to hear it.)Even if there's some reason to keep AAs and sort to use this mechanism, rather than reimplementing them by templates, then reserving a predictable vtbl slot doesn't need to be done by actually filling it. It could be done simply by specifying a slot to be reserved in the ABI. In Object, this slot would be set to null, and the compiler would fill it only when a class Qwert defines an opCmp(Qwert).True, but that's now something that sucks 50% rather than 100%. Let's get 0.Of course, if you then have something like class Yuiop : Qwert { int opCmp(Qwert q) { ... } int opCmp(Yuiop y) { ... } } then the first form would override. This makes sense, as it only makes sense for Yuiop.opCmp(Qwert) to call Yuiop.opCmp(Yuiop) if the argument is a Yuiop. Then we have a principle: the reserved slot would carry the form of opCmp that reflects the scope of mutual comparability. Hence Yuiop[].sort and Qwert[].sort would behave identically, and consistently with uses of the comparison operators directly (under current rules) regardless of which types are declared. Then, when an AA is declared of a certain class as keytype, the compiler would look in that class's vtbl to see if the opCmp slot is set to null; in which case, it would hook up an AA implementation that doesn't rely on opCmp. Similarly, when sort is called on an array, the compiler would report an error if opCmp is null.Hmmm. This seems somewhat similar to my proposal above, apart from the slot business (which I say has no business being a concern) - maybe I should have read all your post before starting my reply. <CG> Seriously, though, the fact that two people have come up with a similar idea for kicking opCmp() out of where it has no business being gives some encouragement, methinks.Maybe some other excuses have been brought up before, but I can't think of them. Is there anything to add to this list?Since your post elicited little response in September, we must surmise that there are none. Walter, I commend this idea for your sincerest consideration. :-) Matthew
Mar 08 2005
I've attached simple template (with example) template sort(T) { void sort(T[] arr, int function(in T l, in T r) cmp) {...} } used as: int arr[] = new int[10000]; sort!(int) ( arr, function int(int i1,int i2) { return i1 - i2; } ); It is simple and works *30% faster* than D builtin sort. Walter, please, pay attention - C standard qsort is a piece of...I could see having the generic array.sort use TypeInfo and its opCmp and have a templated sort in Phobos for those cases when people want a sort that can be optimized for their type (where the comparison function is specified some other way than through the TypeInfo - either explicitly at the call site as you do here or through opCmp operator lookup). Keeping an API where users don't have to know about templates would be nice since templates are an advanced maneuver.
Mar 09 2005
Ben Hinkle wrote: <snip>By a direct translation of the DMD algorithm, or by coding up a better sorting algorithm altogether?int arr[] = new int[10000]; sort!(int) ( arr, function int(int i1,int i2) { return i1 - i2; } ); It is simple and works *30% faster* than D builtin sort.That won't work if two elements of the array can differ by more than 2147483647. This used to be a bug with the TypeInfo for int and uint. But your idea is like one I've had for ages. Isn't it silly if a given D compiler has a state-of-the-art sort implementation but you can't use it, because you want to sort by something other than the type's natural ordering? We could have, on every T[], a built-in method .sort(int function(T, T)) which would sort using the given function as a comparator. And it could be overloaded so it can take either a function or a delegate - this would enable the comparator to be parameterised, and might be useful for database apps. Your code would become arr.sort(function int(int i1, int i2) { return i1 - i2; }); Of course, arr.sort; by itself would remain to denote sorting by the natural ordering.Walter, please, pay attention - C standard qsort is a piece of...I could see having the generic array.sort use TypeInfo and its opCmp and have a templated sort in Phobos for those cases when people want a sort that can be optimized for their type (where the comparison function is specified some other way than through the TypeInfo - either explicitly at the call site as you do here or through opCmp operator lookup).<snip> Why not have opCmp operator lookup as the behaviour of the built-in one? Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Mar 09 2005
But your idea is like one I've had for ages. Isn't it silly if a given D compiler has a state-of-the-art sort implementation but you can't use it, because you want to sort by something other than the type's natural ordering?My point is simple. Existing arr.sort works now (could work better in terms of speed though) and for simple cases, small arrays and atomic types it is pretty handy. For other cases anyone can use given template or the like. I just don't understand the problem. Andrew.
Mar 09 2005
"Andrew Fedoniouk" <news terrainformatica.com> wrote in message news:d0n64j$2i1u$1 digitaldaemon.com...I just pasted your template into std.array and recompiled phobos. I took your main() example and stuck in import std.array; and compiled (with -O -inline -release) and viola everything works great. The only downside is that phobos isn't build with debug information so when I try to compile my example with debug info I get the following link error: Error 42: Symbol Undefined _array_3std5array So if Walter were really going to put a template into phobos he would either have to have debug and non-debug builds of phobos or he'd have to figure out a way around that link error. For those who haven't poked around with Andrew's attached code the actual sort call looks like int[] arr; ... sort!(int)(arr, function int(int i1,int i2) { return i1 - i2; } ); nice! -BenBut your idea is like one I've had for ages. Isn't it silly if a given D compiler has a state-of-the-art sort implementation but you can't use it, because you want to sort by something other than the type's natural ordering?My point is simple. Existing arr.sort works now (could work better in terms of speed though) and for simple cases, small arrays and atomic types it is pretty handy. For other cases anyone can use given template or the like. I just don't understand the problem. Andrew.
Mar 09 2005
int[] arr; ... sort!(int)(arr, function int(int i1,int i2) { return i1 - i2; } ); nice!Yep, exactly. This is the case when templates work. Out of scope, IMO: boost (and STL at some extent) are too generic to be practicaly useful. Even basic stuff - I did by myself full implementation of custom iterator, to use it once or twice - titanic job with zero result practically. onApply way better. One simple function. Little bit "enigmatic" but works.
Mar 09 2005
We could have, on every T[], a built-in method .sort(int function(T, T)) which would sort using the given function as a comparator. And it could be overloaded so it can take either a function or a delegate - this would enable the comparator to be parameterised, and might be useful for database apps. Your code would become arr.sort(function int(int i1, int i2) { return i1 - i2; }); Of course, arr.sort; by itself would remain to denote sorting by the natural ordering.That would be fine, too. I have no problems with that - though I haven't any idea how easy it would be for Walter to do.I don't have any problem with that, either. One advantage I see of the current impl is that the sorting routine is a single routine which cuts down on code duplication. The TypeInfo factors out the type-specific pieces of the sorting algorithm. Having the compiler make custom sorting routines per invocation using either template expansion or something would be fine but it's a trade-off decision Walter has to make. Until Walter decides on a standard way of supplying a comparison fcn, though, Andrew's template will do nicely. I'm sure other people have posted similar things. Maybe Walter will end up extending the sort property on arrays, maybe he'll toss TypeInfos entirely and go with templates, maybe he'll keep TypeInfos and use templates for custom sorting. Who knows :-)I could see having the generic array.sort use TypeInfo and its opCmp and have a templated sort in Phobos for those cases when people want a sort that can be optimized for their type (where the comparison function is specified some other way than through the TypeInfo - either explicitly at the call site as you do here or through opCmp operator lookup).<snip> Why not have opCmp operator lookup as the behaviour of the built-in one?
Mar 09 2005
Matthew wrote: <snip>Since AAs are built-in, why on earth do they need to rely on runtime polymorphism. Given the classes X and Y class X { } class Y { int opCmp(Y rhs); } What prevents the compiler from generating sorting based on arbitrary difference (e.g. address of instance) for X, and using the *non-polymorphic* Y::opCmp(Y) ?? (I know all methods are polymorphic in D, butWhat would be the purpose of making address of instance the default sorting key? Surely sorting an array of X would be a compiler error.<snip> That's exactly my point. It makes no sense. Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.2. "How else can an array of objects be sorted?"Why should an array of arbitrary types be sortable??
Mar 09 2005
"Stewart Gordon" <smjg_1998 yahoo.com> wrote in message news:d0mhe2$1rsr$1 digitaldaemon.com...Matthew wrote: <snip>For AAs, not for arrays. Sorting an array of X would be a compiler error.Since AAs are built-in, why on earth do they need to rely on runtime polymorphism. Given the classes X and Y class X { } class Y { int opCmp(Y rhs); } What prevents the compiler from generating sorting based on arbitrary difference (e.g. address of instance) for X, and using the *non-polymorphic* Y::opCmp(Y) ?? (I know all methods are polymorphic in D, butWhat would be the purpose of making address of instance the default sorting key? Surely sorting an array of X would be a compiler error.Mine too. :-)<snip> That's exactly my point. It makes no sense.2. "How else can an array of objects be sorted?"Why should an array of arbitrary types be sortable??
Mar 09 2005
Matthew wrote: <snip><snip> This applies here as well: http://www.digitalmars.com/d/garbage.html "Depending on the ordering of pointers: if (p1 < p2) // error: undefined behavior ... since, again, the garbage collector can move objects around in memory." Logically, it would hook up an AA implementation that doesn't rely on an ordering. Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.For AAs, not for arrays.What prevents the compiler from generating sorting based on arbitrary difference (e.g. address of instance) for X, and using the *non-polymorphic* Y::opCmp(Y) ?? (I know all methods are polymorphic in D, butWhat would be the purpose of making address of instance the default sorting key? Surely sorting an array of X would be a compiler error.
Mar 09 2005