October 06, 2015
On Tuesday, 6 October 2015 at 17:20:39 UTC, Jonathan M Davis wrote:
>
> But in general, at this point, with D, if you want deterministic destruction, then you use structs. Classes are not the appropriate place for it. If they were ref-counted, then they could be, but as long as they're not, then classes are not the place to have stuff that cares about deterministic destruction. And if you're stuck with stuff in classes that do care about deterministic destruction, then you have to use the sort of solutions that C# and Java use where you don't rely on the destructor/finalizer to clean anything up except for the cases where you screw up and forget to manually call the function that does the cleanup.

Unfortunately, it is quite common to need both virtual functions and deterministic destruction. It isn't helpful to disregard the problem by saying "you should have used a struct", in many cases it's not any easier.


October 06, 2015
On Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote:
>
> I want polymorphism AND deterministic destruction, and the least you could do is just admit that it's a downside to D not having it, instead of trying to tell me that everything I know is wrong..

This problem comes up again and again, here is an ugly trick to ease the pain:
http://p0nce.github.io/d-idioms/#GC-proof-resource-class
October 06, 2015
On Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote:
>
> So, you're saying you want me to just revert back to manual resource management and accept that huge resources like textures and such may just leak if someone doesn't use them right? or throws an exception? in a language like D that is supposed to be safe?
>

Yes. It seems everyone has this epiphany at one point in their D adventures.
D is similar to Java and C# in that regard, and more manual than C++.

On the plus side, you get freedom from thinking about ownership for the 30% or so of resources who only owns memory.
October 06, 2015
On Tuesday, 6 October 2015 at 20:46:00 UTC, ponce wrote:
> On Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote:
>>
>> I want polymorphism AND deterministic destruction, and the least you could do is just admit that it's a downside to D not having it, instead of trying to tell me that everything I know is wrong..
>
> This problem comes up again and again, here is an ugly trick to ease the pain:
> http://p0nce.github.io/d-idioms/#GC-proof-resource-class

Heh...This only adds insult to injury.... I would have thought the GC was smart enough not to have to do this.

    Bit

October 06, 2015
On Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote:
> On Tuesday, 6 October 2015 at 18:43:42 UTC, Jonathan M Davis wrote:
> For the THIRD time, I'll post my example:
>
> class Texture { }
> class Texture2D : Texture {
>     this() { /* load texture... */ }
>     ~this { /* free texture */ }     // OOPS, when, if ever, will this be called?
> }
>
> Now, does this really seem like a realistic use case to you?
>
> using(Texture tex = new Texture2D) {
>     // ...
> }

The reality of the matter is that having the lifetime of an object managed by a GC inherently does not work with deterministic destruction. Garbage collectors simply do not work that way. It's a cost to using them. Code like this is what you have to do when you have a garbage collected language without the ability to put objects on the stack. C# and Java and their ilk are stuck with that. And it definitely sucks, but it can work.

In D, if you're using only classes, you're still stuck with that. However, if you use structs to wrap classes and do reference counting, then it becomes possible to have deterministic destruction for classes, because the struct is doing the deterministic part, and the lifetime of the class object is no longer managed by the GC (assuming that you don't end up with something like the last of the structs with references to that class object inside of classes which aren't ref-counted, in which case, you lost the deterministic destruction and the GC manages the lifetime again). So, we're doing better than C# or Java do, but unfortunately, there are just enough issues with ref-counting structs that to get it fully right, we do need ref-counting in the language (unfortunately, I don't remember all of the corner cases that make that the case). So, ref-counting with structs _mostly_ works, and it is a solution, but it's not quite where we want it to be, which is Walter and Andrei have been talking about adding ref-counting support to the language and DIP 74 was proposed.

> _Is_ it just the interim? Will DIP74 actually ever be implemented? if so, when?

We don't know. It hasn't been officially accepted yet. Walter and Andrei could change their minds and decide that it's not necessary to add ref-counting support to the language. It could be that DIP 74 or something like it ends up being accepted and implemented in a year or three. Or it could be accepted as-is tomorrow. Until Walter and Andrei make a decision on it, we don't know. Given the issues involved, I expect that some form of DIP 74 will be accepted at some point in the future, but they're not going to do that until they're reasonably sure that we've got it right, and that sort of thing is always slow. So, we may very well end up with ref-counting in the language sometime next year, but I'd be shocked if it were this year, and depending on what Walter and Andrei are focusing on, it could be longer than that.

>> In most cases though, just don't use classes. In most cases, inheritance is a horrible way to write programs anyway,
>
> Opinion.

It's well known that it's generally better to use composition than inheritance. Inheritance is useful when you need to have runtime polymorphism - where multiple types need to be used in exactly the same code as if they were the same type with the code using them not caring what they really are. Beyond that, it just starts causing problems - especially with regards to reusibility. As soon as code is in a member function, the only way it can be reused is by deriving from the class that it's in, which is incredibly limiting. Free functions (especially templated free functions) cream member functions for flexibility and reusibility, because they aren't restricted to a single type and its descendants. This is especially true when the language does not support multiple inheritance.

Inheritance makes sense when it's needed, but when you use it, you're losing flexibility and harming code reusibility, meaning that if it's not actually needed, it's just costing you. And I don't see how anyone can really argue otherwise.

Where a lot of that gets debatable is when deciding whether a particular problem actually needs a solution that uses polymorphism, but it's quite clear that using inheritance limits code reusibility.

>> I suspect that far too many folks new to D end up using classes instead of structs just because they're used to
>> using classes in C++ or Java or whatever.
>
> I use classes for polymorphism, and although I can't speak for everyone else, I doubt I'm the only one.

And that's what D classes should be used for. But based on questions on SO and stuff in D.Learn and other places online, it's pretty clear that at minimum, many folks that are new to D use classes even when they don't need polymorphism.

- Jonathan M Davis
October 06, 2015
On Tuesday, 6 October 2015 at 20:44:22 UTC, ponce wrote:
> On Tuesday, 6 October 2015 at 17:20:39 UTC, Jonathan M Davis wrote:
>>
>> But in general, at this point, with D, if you want deterministic destruction, then you use structs. Classes are not the appropriate place for it. If they were ref-counted, then they could be, but as long as they're not, then classes are not the place to have stuff that cares about deterministic destruction. And if you're stuck with stuff in classes that do care about deterministic destruction, then you have to use the sort of solutions that C# and Java use where you don't rely on the destructor/finalizer to clean anything up except for the cases where you screw up and forget to manually call the function that does the cleanup.
>
> Unfortunately, it is quite common to need both virtual functions and deterministic destruction. It isn't helpful to disregard the problem by saying "you should have used a struct", in many cases it's not any easier.

If you need both polymorphism and determinstic destruction, then you're just plain screwed when a GC is managing the lifetime of an object, because garbage collectors simply don't work that way. And anyone using class objects which are managed by a GC needs to understand that. To get the determinism, you need a way to control the lifetime of an object other than the GC (whether the memory for it lives on the GC heap or not). So, at this point, structs _have_ to get involved to get deterministic destruction unless you're going to do it all manually.

And that means that it's better to avoid classes if you don't actually need them, because then you can actually have deterministic destruction (on top of being able to avoid heap allocation if it's not necessary for any member variables). It also means that programs should generally avoid needing to combine polymorphism and deterministic destruction. If you need it, you need it, and you have to either get structs involved as wrappers or manually manage the lifetime of the class objects (or at least manually call a function to tell it to release whatever resources need to be released, even if the object itself isn't actually freed), but if it can reasonably be avoided, it should be avoided.

- Jonathan M Davis
October 07, 2015
On Tuesday, 6 October 2015 at 22:21:41 UTC, Jonathan M Davis wrote:
>
> So, we're doing better than C# or Java do, but unfortunately,
> there are just enough issues with ref-counting structs that to
> get it fully right, we do need ref-counting in the language (unfortunately, I don't remember all of the corner cases that make that the case). So, ref-counting with structs _mostly_ works, and it is a solution, but it's not quite where we want it to be, which is Walter and Andrei have been talking about adding ref-counting support to the language and DIP 74 was proposed.

I'll give you that. The situation is slightly better than C#.

But these problems still exists:

-structs can be nested in classes, and inherit the non-deterministic lifetime of classes. The designer of a library has no control over this.

-classes that are meant to be ref counted(meaning that they depend on their destructor being called in a timely fashion) are not guaranteed to be wrapped in a RefCounted or similar RAII object. Again, the designer of a library has no control over this.

-again, alias this allows class references to escape their RAII containers and can cause access violations as show here:
http://forum.dlang.org/post/zfggjsjmfttbcekqwgjd@forum.dlang.org

-The syntax is annoying(this is a lot more important that people in this forum usually want to believe)

-wrapper structs hide the real type of the object from templates, typeof(), etc..

-etc..


>> _Is_ it just the interim? Will DIP74 actually ever be implemented? if so, when?
>
> We don't know. It hasn't been officially accepted yet. Walter and Andrei could change their minds and decide that it's not necessary to add ref-counting support to the language. It could be that DIP 74 or something like it ends up being accepted and implemented in a year or three.

If it takes long enough that C++ has reflection, modules, ranges, stackless coroutines, concepts, etc, then I gotta be honest, I'm gonna start worrying about investing too much time in D.

> I'd be shocked if it were this year, and depending on what
> Walter and Andrei are focusing on, it could be longer than that.

I hope you're wrong. It's one thing to move slow, but looking at DIP74, it doesn't seem to be moving at all(last updated 2015-03-04)


> But based on questions on SO and stuff in D.Learn and other
> places online, it's pretty clear that at minimum, many folks that are
> new to D use classes even when they don't need polymorphism.

Yes, that's why they're asking question in D.Learn and SO... :)

There seems to be a general trend in software these days where developers are getting lazy, and saying "But no one is asking for it" and using that to justify leaving features out.  This approach doesn't take into account people that just _expect_ certain features to be present because they're so obvious.

If you went to a Pizza Hut and they gave you a pizza with no cheese on it "because no one asks for it", would you actually say anything, or just dismiss them offhand and go somewhere else?

There is something to be said for tradition. Not everything has to be a democracy..

    Bit




October 07, 2015
On Tuesday, 6 October 2015 at 20:44:22 UTC, ponce wrote:
> On Tuesday, 6 October 2015 at 17:20:39 UTC, Jonathan M Davis wrote:
>> [...]
>
> Unfortunately, it is quite common to need both virtual functions and deterministic destruction. It isn't helpful to disregard the problem by saying "you should have used a struct", in many cases it's not any easier.

+1 to this and pretty much everything else you've said in this thread.
Tired of people just shrugging it off when it's a serious issue.
October 07, 2015
On 10/4/2015 11:02 AM, bitwise wrote:
> For example, streams.

No streams. InputRanges.

October 07, 2015
On Wednesday, 7 October 2015 at 01:27:27 UTC, Walter Bright wrote:
> On 10/4/2015 11:02 AM, bitwise wrote:
>> For example, streams.
>
> No streams. InputRanges.

This is too vague to really respond to. It does serve as an example of the over-emphasis on ranges in the D community. Ranges are great, but not for everything.

     Bit