January 14, 2022
On Friday, 14 January 2022 at 08:00:30 UTC, bauss wrote:
> This is a very important point because in general that could bloat your memory by A LOT.

Yeah, I didn't even think about this.

The interfaces, as defined, are utterly useless anyway and will probably never be used but yeah interesting find.
January 14, 2022
DIP wrote:
> As a consequence, these methods make it difficult to use Object with qualifiers or in code with properties such as @nogc, pure, or @safe

But... RazvanN wrote:
> I find it hard to believe that people write libraries
> where they simply type a parameter as being Object.

This DIP is so bad its own steward is arguing against it!

Meanwhile on the topic is just changing things in Object,

RazvanN wrote:
> failure will be clear and arguably it would be justifiable
> because the defaults for opCmp/toHash simply return the
> address of the class.


Actually the address is a pretty useful default hash but indeed, if changing these are important, a mildly breaking change with clear deprecation migration path would be justified.
January 14, 2022
On Fri, Jan 14, 2022 at 04:19:42AM +0000, Dom DiSc via Digitalmars-d wrote:
> On Friday, 14 January 2022 at 03:41:49 UTC, H. S. Teoh wrote:
> 
> > No, they are not inherently orderable, the orderings you mentions are external orders imposed on them.
>
> Nothing is inherently ordered. The order is always some new property given to the object in question. Numbers are nothing then a (unordered) set plus an order given to them (an axiom on its own). The order is never "inherent" and there are always endless different ways to assign an order to a set of objects.

Now you're just splitting hairs.  Number types in a *programming language* (we're not talking about abstract mathematics here) are inherently-ordered types.  This is why `1 < 2` always evaluates to true, and `2 < 1` always evaluates to false in D. It makes no sense to impose a different ordering on the ints themselves.  But in a list of numbers, you *can* order them by a different ordering, e.g., if they represent indices into some other orderable object.  Such non-default orderings, though, do not belong to the int type itself. They are external orderings imposed upon ints that are not its inherent ordering.

Many classes don't an inherent ordering, because there isn't any single ordering that makes the most sense for objects of that class.  Objects like File can have any number of different orderings imposed on them -- order by size, by name, by creation time, by last access time, etc., precisely because a file itself isn't inherently ordered w.r.t another file.  These are all external orderings, and therefore do not belong to File.opCmp.  Instead, they should be predicates that the user can choose when sorting a list of File objects.

Since most classes don't have an inherent order, it doesn't make sense to assume they do, which is what we're doing when we put .opCmp in the base class of all classes.  Since only a subset of classes are inherently ordered, it makes more sense to put .opCmp in either a subclass or an interface that derived classes can derive from.


> The important part is: it is mostly useful to do so (assigning a specific order to a set of objects), thereby making from a set an ordered set.  And especially on computers a set always need at least some "default" order (e.g. address), otherwise you cannot access its elements.

This is a nonsensical argument.  So the default order of every data type should be its address?  You don't need .opCmp for that, just cast the pointer and compare its value.  Every object already has an address, there is no need to spend a vtable slot for this.

The whole issue here is, does *every* class you can conceivably define in D need to have an .opCmp method?  The answer is no, you don't. If you want to sort some objects by some order, you can always pass a predicate to std.algorithm.sort.  If you have some classes that are inherently ordered and therefore could benefit from sharing a common .opCmp method, then inherit them from an Orderable base class or Orderable interface. There is no need for the base class of every class in the language to assume that every subclass needs an .opCmp.


> But I agree that it is useful to have a way to assign some predicate to a class by which its instances should be ordered (at least if you don't like the default order: the memory address where it is stored), but that need not be a member function, so I omitted it.

Well yes, which is exactly the point: most classes *don't need* an .opCmp -- you can just pass a predicate.  The classes that could benefit from .opCmp are a subset of all classes, therefore they should inherit from a common Orderable base class or interface.  So .opCmp doesn't belong in the base class of all classes in the language. QED.


T

-- 
Marketing: the art of convincing people to pay for what they didn't need before which you fail to deliver after.
January 14, 2022

On Friday, 14 January 2022 at 03:38:28 UTC, tsbockman wrote:

>

On Monday, 10 January 2022 at 13:48:14 UTC, Mike Parker wrote:

>

This is the discussion thread for the first round of Community Review of DIP 1042, "ProtoObject":

https://github.com/dlang/DIPs/blob/2e6d428f42b879c0220ae6adb675164e3ce3803c/DIPs/DIP1042.md

One issue with this DIP that I haven't noticed anyone else raise yet is interface bloat. Each interface directly implemented by a class or any of its super classes adds size_t.sizeof to the instance size:

Ouch! Good catch, that is a large issue.

Though I think the culprit here is the habit of class ABI to bloat this way, not the DIP. Such instance size bloat punishes many otherwise object hierarchies anyway, we want to get rid of that regardless of what happens to this proposal.

Still, a serious setback for this DIP. I'd add a proposal in the DIP to do something about the bloat. The bloat needs to be solved before implementing the DIP, otherwise it's a sizeof(size_t) more size for every Object instance (as Object can't be implicit in vtable anymore).

January 14, 2022

On Friday, 14 January 2022 at 20:22:18 UTC, Dukc wrote:

>

Such instance size bloat punishes many otherwise object hierarchies anyway,

Meant: many otherwise great class hierarchies anyway

January 14, 2022
On Friday, 14 January 2022 at 19:22:59 UTC, H. S. Teoh wrote:
> This is a nonsensical argument.  So the default order of every data type should be its address?  You don't need .opCmp for that, just cast the pointer and compare its value.
Just FYI: at the moment, yes, the default implementation of opCmp does just that: comparing the address, however sensible or nonsensical that may be. Only if you want something different, you need to implement opCmp yourself.

But ok, maybe you convinced me that not every class needs an opCmp.
Maybe the whole concept of classes is not so very useful anymore if you have structs, alias this and UFCS-(pseudo-)member-functions.
January 14, 2022
On Tuesday, 11 January 2022 at 15:18:00 UTC, Adam D Ruppe wrote:
>
> The reality is the only thing here that would actually require a breaking change is removing the monitor.

After speaking a bit with Adam, I discovered the proponents of an alternative to this DIP.
Let's call us this "covariant camp" (or was it contravariance?).


## Attributes

I sum up the position of the "dual" camp people in an example:

AFAIK the DIP is intended to (say) allows a heterogeneous @nogc hashmap of ProtoObject, using the new @nogc toHash call.

BUT you can add attributes to a virtual function in derived class and its children.

HENCE if all your heterogeneous object have a tightened restriction in toHash in a derived class, just derive all from that class and it would also works.
Instead of a hashmap of ProtoObject, you would have a hashmap of HashObject, where HashObject is a derivative of Object with tightened restrictions.

    class HashObject : Object
    {
        override const @nogc nothrow pure @safe scope size_t toHash();
    }



## Monitor

The alternative then, if Object stays the root class, would be to depreciate the monitor and remove Object.factory. Eventually synchronized(obj) can only take SynchronizedObject as parameter.
(Adam further makes the argument that instead of a transition to ProtoObject to get our 16 bytes back and win the monitor bytes, it would be an automatic gain.)

(Sorry if I misrepresent point of views here. I myself have no formed opinion about all this)
January 14, 2022
On Friday, 14 January 2022 at 16:00:49 UTC, Adam D Ruppe wrote:
>
> The interfaces, as defined, are utterly useless anyway and will probably never be used but yeah interesting find.

Imho interfaces could be smth like this:

```d
interface Equals(U) {
  bool equals(U other);

  bool opEquals(this T)(U other) {
    return (cast(T) this).equals(other);
  }
}
```

In this case you can narrow down by attributes the equals implementation, and the opEquals will pick them, given T is known at compile time (i.e. the implementor/extender of interface).

Best regards,
Alexandru.
January 14, 2022

On Friday, 14 January 2022 at 08:00:30 UTC, bauss wrote:

>

That's just terrible.

I don't think it's a valid criticism actually.
Not too many objects actually need to be all of the following: hashable, ordered, have a string representation, need ==.

If they need all 4, and they need it virtually, then pay the bytes.

January 14, 2022
On Friday, 14 January 2022 at 22:41:45 UTC, Guillaume Piolat wrote:
>
> (Sorry if I misrepresent point of views here. I myself have no formed opinion about all this)

Aftert hinking a bit more: Still supporting the DIP. If this is implemented, not only none of my code breaks (!!!) but I can derive from ProtoObject progressively and win a few bytes of memory. I think the restrictions on hash and opCmp are fair.