www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Rethinking the default class hierarchy: Milestone 1, Week 1

reply Robert Aron <aronrobert293 gmail.com> writes:
[Project 
description](https://gist.github.com/edi33416/0e592f4afbeb2fb81d3abf235b9732ce)
[SAOC 
projects](https://dlang.org/blog/2021/08/30/saoc-2021-projects/)

Hello!

The project that I have selected is “Rethinking the default class 
hierarchy”.
Every class defined in the D language has `Object`  as the root 
ancestor. `Object` defines four methods: `toString`, `toHash`, 
`opCmp` and `opEquals`, that, at a first glance, might not strike 
you with much but they are doing much more harm than good. Their 
signatures predate the introduction of the ` nogc`, `nothrow`, 
`pure` and ` safe` function attributes and `const`, `immutable` 
and `shared` type qualifiers. As a consequence, these methods 
make it difficult to use `Object` with qualifiers or in code with 
properties such as ` nogc` or ` safe`.\
The goal of the project is to implement the solution proposed by 
Eduard Staniloiu in his talk. We will introduce a new class, 
`ProtoObject`, as the root class and ancestor of `Object`. 
`ProtoObject` defines no methods and requires the user to 
implement the desired behaviour through interfaces: this approach 
enables the user to opt-in for the behaviour that makes sense for 
his class and the design is flexible enough to allow future 
attributes and language improvements to be used without breaking 
code.


Last week I managed to do the following tasks for #SAOC2021:
- read the 
[gist](https://gist.github.com/edi33416/0e592f4afbeb2fb81d3abf235b9732ce) that
Edi provided and also watch [his
talk](https://www.youtube.com/watch?v=EcG5mnOzZ0s) from Dconf to familiarize
myself with the problem and the way we are going to handle it. To summarize
this, we are going to introduce a new class `ProtoObject` as the root of all
classes. The recommended way of going forward with types that inherit from
`ProtoObject` is write and implement interfaces that expose the desired
behaviour(order, equality, hash, etc.) that the type supports.
- read about how the default class hierarchy is designed in Java, 

    - Java has quite a few similarites with Dlang, both defining 
operations like `opEquals` or `getHash`, having similar 
operations (for example `getClass` is simillar to `typeid(obj)`) 
and an `object monitor`
    - Kotlin is similar to Java, as it was designed to 
interoperate fully with Java. In Kotlin the root of all classes 
is represented by the `Any` class and it also the operations 
described above, as well as new ones, such as `apply` and `also`

Kotlin and D, and it comes a lot closer to what we desire. There 
is no builtin monitor. The `Monitor` object is implemented as a 
separate class that inherits from `Object` in the 

imposed methods, but they are still imposed, and `toString` will 
continue to be GC dependent.
- took a look at the [DIPs](https://github.com/dlang/DIPs) to 
familiarize myself with them and read the guidelines

The plan for the next week:
- read about how Rust handles this, since it has a totally 
different approach
- start writing the DIP for the project
Sep 23
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 9/23/21 3:15 AM, Robert Aron wrote:

 `ProtoObject` defines no methods
 `object monitor`
Can ProtoObject get rid of monitor as well? Ali
Sep 24
next sibling parent Robert Aron <aronrobert293 gmail.com> writes:
On Friday, 24 September 2021 at 20:17:51 UTC, Ali Çehreli wrote:
 On 9/23/21 3:15 AM, Robert Aron wrote:

 `ProtoObject` defines no methods
 `object monitor`
Can ProtoObject get rid of monitor as well? Ali
Yes, it can. `ProtoObject` will be empty.
Sep 29
prev sibling parent reply Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Friday, 24 September 2021 at 20:17:51 UTC, Ali Çehreli wrote:
 On 9/23/21 3:15 AM, Robert Aron wrote:

 `ProtoObject` defines no methods
 `object monitor`
Can ProtoObject get rid of monitor as well? Ali
Since there's no default base class in C++ (like `object.Object`), `extern (C++) class`-es defined in D don't inherit one implicitly as well, which is obviously necessary for ABI compatibility. This means that D already supports truly empty non-abstract classes (like the proposed `ProtoObject`), but only if they're declared as `extern (C++)`. Essentially, this proposal is about removing this limitation, so that an `extern (D)` class can also be empty. Luckily, most of Druntime and Phobos are oblivious to the fact that D's classes have monitors, as monitors are only needed for thread synchronization. (That's because D made the right choice by making `shared` memory explicit in the type system.) When is the last time you saw a Druntime, Phobos, or in general, D function that took a `shared` class reference? (Not a completely rhetorical question - I'd be interested to know if people have such instances in their projects.) Outside of `core.sync.*`, `std.concurrency` and `std.parallelism` nothing deals with `shared` classes. AFAIU, inserting `ProtoObject` as the base class of `Object` should be backwards compatible change as far as most library / application code is concerned, outside of specific meta-programming related code, like [this][1]. I think the biggest challenge would be ironing all the type-system related details, e.g. basic things like this: ``` // Should print `ProtoObject`? pragma (msg, typeof(( new class { auto getParent() { return super; } }).getParent() ) ); ``` https://run.dlang.io/is/DvVcn9 ``` class A {} class B {} // Object[] pragma (msg, typeof([ new A, new B])); extern (C++) class C {} extern (C++) class D {} // Error: incompatible types for `(new C) : (new D)`: `onlineapp.C` and `onlineapp.D` // _error_ (ICE?) pragma (msg, typeof([ new C, new D])); class E : ProtoObject {} class F {} // should print: `ProtoObject[]` pragma (msg, typeof([ new E, new F])); ``` https://run.dlang.io/is/LpmnfR [1]: https://dlang.org/phobos/std_traits#.AllImplicitConversionTargets
Sep 29
parent reply Sebastiaan Koppe <mail skoppe.eu> writes:
On Wednesday, 29 September 2021 at 16:11:24 UTC, Petar Kirov 
[ZombineDev] wrote:
 When is the last time you saw a Druntime, Phobos, or in 
 general, D function that took a `shared` class reference? (Not 
 a completely rhetorical question - I'd be interested to know if 
 people have such instances in their projects.) Outside of 
 `core.sync.*`, `std.concurrency` and `std.parallelism` nothing 
 deals with `shared` classes.
I tend to use the shared annotation a lot in our concurrency library. Structs and delegates mostly though, but it has some classes as well. Obviously it is the sane thing to do once you have multiple execution contexts with shared state. I guess not many people have that though.
Sep 29
parent reply Bruce Carneal <bcarneal gmail.com> writes:
On Wednesday, 29 September 2021 at 21:40:11 UTC, Sebastiaan Koppe 
wrote:
 On Wednesday, 29 September 2021 at 16:11:24 UTC, Petar Kirov 
 [ZombineDev] wrote:
 When is the last time you saw a Druntime, Phobos, or in 
 general, D function that took a `shared` class reference? (Not 
 a completely rhetorical question - I'd be interested to know 
 if people have such instances in their projects.) Outside of 
 `core.sync.*`, `std.concurrency` and `std.parallelism` nothing 
 deals with `shared` classes.
I tend to use the shared annotation a lot in our concurrency library. Structs and delegates mostly though, but it has some classes as well. Obviously it is the sane thing to do once you have multiple execution contexts with shared state. I guess not many people have that though
Like Sebastiaan, I also use shared quite a bit. It helps keep things sorted if you need/want to roll your own multi-core flows.
Sep 29
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
I've stopped using shared entirely, it has only one meaning now for me 
and that is: use atomics.

Having it infiltrate everywhere isn't helpful, in fact half the time I 
spent is on fixing casting to and from shared.

This is after trying to implement lock-free concurrent data structures 
in D for like 6 months, so my opinion is slightly tainted with shared 
and atomics in general.
Sep 29
parent reply Sebastiaan Koppe <mail skoppe.eu> writes:
On Thursday, 30 September 2021 at 04:02:30 UTC, rikki cattermole 
wrote:
 I've stopped using shared entirely, it has only one meaning now 
 for me and that is: use atomics.

 Having it infiltrate everywhere isn't helpful, in fact half the 
 time I spent is on fixing casting to and from shared.

 This is after trying to implement lock-free concurrent data 
 structures in D for like 6 months, so my opinion is slightly 
 tainted with shared and atomics in general.
I remember fighting with `shared` too initially, trying to force my ideas onto the keyword. It resulted in a lot of casts and me hating it. I did came around though.
Sep 30
parent rikki cattermole <rikki cattermole.co.nz> writes:
On 30/09/2021 8:23 PM, Sebastiaan Koppe wrote:
 On Thursday, 30 September 2021 at 04:02:30 UTC, rikki cattermole wrote:
 I've stopped using shared entirely, it has only one meaning now for me 
 and that is: use atomics.

 Having it infiltrate everywhere isn't helpful, in fact half the time I 
 spent is on fixing casting to and from shared.

 This is after trying to implement lock-free concurrent data structures 
 in D for like 6 months, so my opinion is slightly tainted with shared 
 and atomics in general.
I remember fighting with `shared` too initially, trying to force my ideas onto the keyword. It resulted in a lot of casts and me hating it. I did came around though.
9 months ago, my conclusion was the same as yours. Unfortunately my experiences didn't stop there... lol
Sep 30