Thread overview
Discuss: Classes are well supported in D, with Design by Contracts. Shouldn't we take advantage of that?
6 days ago
Brother Bill
6 days ago
Mike Parker
6 days ago
monkyyy
5 days ago
Dejan Lekic
5 days ago
H. S. Teoh
5 days ago
Neto
6 days ago

I'm not clear about why 'class'es are on the 'avoid' list.

D has excellent support for Single inheritance, Interfaces, Design by Contract (DbC), GC, etc.
I'm aware that there is a small run time cost for selecting the right virtual method.

To reduce this cost, one must final-ize methods that don't need to be overridden.

Using classes is a major reason that I chose D.
Outside of Eiffel language, D comes closest to supporting DbC.
Even .NET has abandoned supporting DbC.

6 days ago

On Saturday, 13 September 2025 at 00:43:38 UTC, Brother Bill wrote:

>

I'm not clear about why 'class'es are on the 'avoid' list.

D has excellent support for Single inheritance, Interfaces, Design by Contract (DbC), GC, etc.
I'm aware that there is a small run time cost for selecting the right virtual method.

To reduce this cost, one must final-ize methods that don't need to be overridden.

Using classes is a major reason that I chose D.
Outside of Eiffel language, D comes closest to supporting DbC.
Even .NET has abandoned supporting DbC.

What 'avoid list'? Plenty of D programmers use classes. If they make sense for a specific scenario according to how you want to architect your software, then go ahead and use them.

Some people prefer to avoid them because they, by default, require GC allocation. There are two basic concepts to keep in mind:

  • the more often you allocate from the GC, the more opportunities it has to run;
  • the more memory you allocate from the GC, the more memory it has to scan.

In other words, allocate only what you really need and avoid allocations in hotspots. The goal is to minimize GC pressure.

There are different strategies to get there, like using structs for POD types or when you don't really need inheritance, preallocating before hot spots, using free lists to recycle objects, etc. Some of that is architectural and should be decided beforehand, some of it is only what you think about when you need to optimize.

Regardless, you'll be fine most of the time without needing to worry about most of that. Just remember that D is not Java and everything doesn't need to be a class.

6 days ago

On Saturday, 13 September 2025 at 01:00:17 UTC, Mike Parker wrote:

>

What 'avoid list'?

Mine in the last thread

5 days ago

On Saturday, 13 September 2025 at 00:43:38 UTC, Brother Bill wrote:

>

I'm not clear about why 'class'es are on the 'avoid' list.

They are on some peoples' avoiding list. It does not mean it should be on yours.

I can tell you one thing with 100% certainty - if D had no classes I would not be using it.

5 days ago
On Sat, Sep 13, 2025 at 12:43:38AM +0000, Brother Bill via Digitalmars-d-learn wrote:
> I'm not clear about why 'class'es are on the 'avoid' list.

Whose avoid list?

I'd say that I usually don't use classes, not because I'm trying to avoid them, but because I don't need to. Most of the time my programming problem can be solved with by-value structs, DbI (design by introspection), and pseudo-functional style range-based programming. But that doesn't mean I don't use classes at all.  If my programming problem is best solved using classes, why not use them?  They're in D for a reason.

The thing about D is that it is multi-paradigm.  This means that your toolbox has more than just classes and DbC. You also have DbI, templates, metaprogramming, compile-time introspection, functional-style range-based programming, string mixins, low-level assembly, etc..  Each of these tools have their areas of strength and areas of weakness; you choose them based on the specific programming problem at hand, and you're not obligated to use any one of them if you choose not to.

There's this joke that applying computer technology is finding the right wrench to pound in the correct screw, which may be an apt description of what one is forced to do in many single-paradigm languages where the only tool you have is a wrench, and you have to use it whether or not your programming problem is a bolt.  But I like to think that in D, you actually have a screwdriver that you can use when your problem is a screw, and it also has a hammer when you need to pound in a nail.  Use the right tool for the right job.


> D has excellent support for Single inheritance, Interfaces, Design by
> Contract (DbC), GC, etc.

I don't use DbC often, but I do appreciate that D has it when I do use it.


> I'm aware that there is a small run time cost for selecting the right virtual method.

Yes, but it also depends on whether this is on your program's hot path or not.  If not, uglifying your code for the sake of "performance" is usually wasted effort.  Your decision rather should be driven by which construct better suits your programming problem, than blindly foregoing virtual methods (or any other construct) because of "performance" without even having profiled your program to learn its actual bottlenecks. (Which are almost always not where you think they are. I used to think I knew, until I started using real profilers and discovering that I was wrong most of the time.)


> To reduce this cost, one must final-ize methods that don't need to be overridden.

Again, profile before making decisions like these.


> Using classes is a major reason that I chose D.
[...]

Great.  Welcome to D!


T

-- 
BREAKFAST.COM halted...Cereal Port Not Responding. -- YHL
5 days ago
On Saturday, 13 September 2025 at 15:26:15 UTC, H. S. Teoh wrote:
> On Sat, Sep 13, 2025 at 12:43:38AM +0000, Brother Bill via Digitalmars-d-learn wrote:
>> [...]
>
> Whose avoid list?
>
> [...]

what are those real profiles that you're using now?
5 days ago
On Friday, September 12, 2025 6:43:38 PM Mountain Daylight Time Brother Bill via Digitalmars-d-learn wrote:
> I'm not clear about why 'class'es are on the 'avoid' list.

It's more that there's rarely any reason to use classes in D for the average program. If you need inheritance and polymorphism, you use classes. You might also use classes if you want to force the type to be a reference type. But most types don't need inheritance or polymorphism, and templates make it so that code can be shared without needing a common base type. And if you use a struct, you have the choice of whether it's going to live on the stack or on the heap, whereas classes normally have to live on the heap.

The result of all of this is that structs are the default choice in D rather than classes. There's just no reason to use a class if you don't actually need what makes classes different from structs. So, if you don't need a class, you don't use a class, whereas if you do need a class, then you use a class. Programmers who use classes in D when they didn't actually need to are almost always programmers who are new to D.

So, if you're seeing advice about avoiding classes in D, it's typically going to be because some of the folks coming to D assume that they should use object-oriented design with inheritance all over the place, because that's what they're used to doing in languages which are less flexible (or they use classes simply because they're used to using the class keyword, so they grab that first), and there's no need to take that approach with D by default. So, D programmers are typically going to give the advice that you shouldn't use classes unless that's what you actually need. But that doesn't mean that classes shouldn't be used. It just means that you should use structs rather than classes unless what you're doing is actually better served by using classes. There's no reason to avoid classes when they're actually the best tool for the job. It's just that they're usually not the best tool for the job in D, whereas folks coming from other languages are used to using classes, so that's often what they first do in D.

> D has excellent support for Single inheritance, Interfaces,
> Design by Contract (DbC), GC, etc.
> I'm aware that there is a small run time cost for selecting the
> right virtual method.
>
> To reduce this cost, one must final-ize methods that don't need to be overridden.
>
> Using classes is a major reason that I chose D.
> Outside of Eiffel language, D comes closest to supporting DbC.
> Even .NET has abandoned supporting DbC.

Well, D's DbC is kind of broken insofar as it's compiled into the callee code and not the caller code. So, if a library has contracts, whether they're compiled in or not is based on whether that library was compiled with them enabled or not, whereas what's really needed is for them to compiled in based on whether the caller is compiled with contracts enabled or not, because at least with in contracts, the whole point is to test that the caller code is doing the correct thing, not that the library is doing the correct thing.

For most functions, using an in contract adds nothing over simply putting the assertion at the start of the function (the one exception being with virtual functions, since the compiler will do boolean logic based on which contracts succeed or fail in the inheritance tree for that function). So, in the vast majority of cases, the assertions might as well just go at the start of the function, because using an in contract instead adds no value. Of course, if they were compiled in at the call site, then they _would_ actually have some value, because they'd provide actual contracts which the caller could use to test their code. However, in practice (aside from the inheritance case), while they're supposed to be testing the caller's code, they're being treated just like any assertion in the callee code, making it so that you might as well just use assertions in the function body, because it's basically the same thing. So, even if in contracts are theoretically of value, in practice, they really aren't, though changes to how D compiles contracts could change that.

Out contracts are also generally useless (and really would be no matter how they were compiled in), because in the vast majority of cases, to test whether the output is correct or not, you need to know both what the input was and what the correct result for that input is, and that job is much better done with unit tests. There are rare exceptions where an out contract might still make sense, because there's some condition which you want to test for which isn't dependent on the input, and with virtual functions, you do get the added boolean logic with the inheritance tree, but realistically, out contracts could be removed from the language and have almost no impact on existing code. The cases where they could even theoretically be useful are just too rare.

Invariants on the other hand can add real value, because they add checks for all public member function calls, and inserting those checks yourself for each function would be far more verbose. But even then, you do have to be careful with them, because they tend to fall apart with any types which give any access to their member variables, since that allows code to bypass the invariant and potentially invalidate it. So, they're definitely the most useful part of D's DbC implementation, but even then, I don't think that they get used very often.

In practice, I've occasionally seen folks use in contracts (though they all could have been normal assertions, since I don't think that I've ever seen contracts used with classes in the wild), and I've occasionally seen invariants used, but most D programmers don't use D's DbC features. Of course, some do, and if you want to use them, they're there. But in contracts in particular are flawed with how they currently work in D. So, I wouldn't suggest using D's DbC outside of cases where invariants make sense, but do whatever works for you.

- Jonathan M Davis