Features of D That I Love
73 comments
·July 2, 2025elcritch
girvo
I was about to say "Yeah, its my favourite feature of Nim!" and then I realised what account I was replying to ;)
kmarc
Reminds me of vim script's implicit method syntax [1]
Eg. any function call can be converted to a method call on the function's first parameter:
let mylist = [3, 2, 1]
" prints "1" as these two are equivalent
echo sort(mylist) == mylist->sort()
Helps a lot with chaining.layer8
Dot syntax tends to work better for code completion, though.
In addition, without uniform call syntax, adding a new method can only break subclasses, whereas with uniform call syntax it can break other client code.
jayd16
If all you're doing is accessing public members, sure.
nicwilson
`private` is only private to the module, not the struct/class, (In other words, all functions in the same module are all C++ style `friend`s) and so free function in same module work.
WalterBright
It works for all functions that have parameters, `f(a)` and `a.f()` are equivalent.
jayd16
Yes, but I presume that f() cannot access private members of a.
OskarS
The "invariants" thing is fantastic, I haven't seen anything like that before and it's great. The C++26 contract stuff is fine, but this seems like a really great way of ensuring type invariants, I think I'd use this way more if it was in C++.
discardable_dan
The issue is most developers do not bother to write any, and the ones that are written are most-often vapid typing failures ("these`int`s cannot be negative" should be handled by a type). I studied this field in grad school, and the entire problem almost always devolves into convincing developers to engage with the system.
wavemode
I find that is the case with almost all methodologies for software quality improvement. If you can't enforce that people follow it then it's not worth anything.
esafak
It only takes one enlightened CTO :)
destructionator
Just a personal anecdote, Walter Bright's Digital Mars C++ compiler also had the contracts (D started life almost literally as recycled code from Mr. Bright's other compilers - he wrote a native Java compiler, a Javascript 1.3 stdlib, and a C++ compiler with a bunch of extensions.... smash those together and you have the early D releases!).
Anyway, I used the DM C++ compiler originally because it was the only one I could download to the high school computers without filling out a form, and pimply-face youth me saw "DESIGN BY CONTRACT" at the top of the website and got kinda excited thinking it was a way to make some easy money coding online.
Imagine my disappointment when I saw it was just in/out/invariant/assert features. (I'm pretty sure D had just come out when I saw that, but I saw `import` instead of `#include` and dismissed it as a weenie language. Came back a couple years later and cursed my younger self for being a fool! lol)
WalterBright
The in/out features come into their own when inheritance is in play, i.e. for member functions of classes and interfaces. See https://dlang.org/spec/function.html#in_out_inheritance
`import` is so cool we extended it to be able to import .c files! The D compiler internally translates them to D so they can be used. When this was initially proposed, the reaction was "what's that good for?" It turned out to be incredibly useful and a huge time saver.
The concept is sort of like C++ being a superset of C and so being able to incorporate C code, except unlike C++, the C syntax can be left behind. After all, don't we get tired of:
struct Tag { ... } Tag;
?
WalterBright
My C++ compiler also implemented contracts back in the 90s: https://www.digitalmars.com/ctg/contract.html
Modern C++ is slowly adopting D features, many of which came from extensions I added to my C++ compiler.
peterashford
I think they were introduced with Eiffel, which was all about design by contract
johnisgood
Ada has that too, for what it is worth: https://learn.adacore.com/courses/intro-to-ada/chapters/cont... and https://en.wikibooks.org/wiki/Ada_Programming/Contract_Based.... It can be verified at compile time.
But what I love the most is: https://news.ycombinator.com/item?id=43936007
Instead of:
const MIN_U32 = 0;
const MAX_U32 = 2 ** 32 - 1;
function u32(v) {
if (v < MIN_U32 || v > MAX_U32) {
throw Error(`Value out of range for u32: ${v}`);
}
return leb128(v);
}
You can do this, in Ada: subtype U32 is Interfaces.Unsigned_64 range 0 .. 2 ** 32 - 1;
or alternatively: type U32 is mod 2 ** 32;
and then you can use attributes such as: First : constant U32 := U32'First; -- = 0
Last : constant U32 := U32'Last; -- = 2 ** 32 - 1
Range_ : constant U32 := U32'Range; -- Range 0 .. 2**32 - 1
Does D have anything like this? Or do any other languages?12_throw_away
Yeah, these look excellent. Am curious if D's invariants can be traced back to Ada/Spark at all (I don't know much about Ada except that it has these sorts of safety features).
johnisgood
Maybe this might help: https://news.ycombinator.com/item?id=44449835
fuzztester
>The "invariants" thing is fantastic, I haven't seen anything like that before and it's great.
is it not the same as the one in Eiffel?
null
almostgotcaught
> Invariants are functions that run at the start and end of every public member function
these are just runtime assertions
EDIT: how am i getting downvoted for copy-pasting literally what the article verifies?
LorenDB
Yes, but they are guaranteed to run at the beginning and end. C/C++ asserts need to handle any return path, whereas D has functionality to mark statements to run at the end of any return path while only being written once.
See also the scope(exit) feature.
almostgotcaught
You can accomplish the same exact thing with
https://en.cppreference.com/w/cpp/experimental/scope_exit.ht...
jayd16
I think there's something to be said about them running automatically that is lost when you say they're just asserts.
almostgotcaught
i don't get it - if do
int foo(int a) {
assert(a > 5);
int b = a * 10;
assert(b > 50);
return b;
}
do you think those asserts don't "run automatically"?readthenotes1
Maybe it's the editorial "just"?
Like: software programs can't be that difficult to create properly because they are just 1s and 0s.
johnisgood
This is not the first time someone getting down-voted for using the word "just". I do not know if this really is warranted, however.
eric-p7
It's a mystery why D isn't far more popular than it is. Fast compilation, familiar syntax, and supports a wider range of programming paradigms than most (any?) other language.
dataflow
> It's a mystery why D isn't far more popular than it is.
There's no mystery. It's a jack of all trades, master of none. E.g., the virality of the GC makes it a non-starter for its primary audience (C/C++ developers). The need to compile and the extra verbosity makes it a bad substitute for scripting like Python. Etc.
Basically, name any large niche you'd expect it to fill and you'll probably find there's a tool already better suited for that niche.
bachmeier
> the virality of the GC makes it a non-starter for its primary audience (C/C++ developers)
No. If you were to say you need the GC to use all features of the language and standard library, of course, the GC does important things, but to claim a C developer wouldn't be comfortable with it because of the GC is nonsense. Just don't allocate with the GC and use the same mechanisms you'd use with C (and then build on top of them with things like @safe, reference counting, and unique pointers).
dataflow
>> the virality of the GC
> Just don't allocate with the GC
"virality" is not just a word you can ignore.
unclad5968
If I'm just coding C except a new syntax, why wouldn't I just stick with C?
WalterBright
We don't have a marketing budget, although we have many hard core users!
johnisgood
Is that really it? Why cannot you get a marketing budget, sponsored perhaps?
BTW:
I am not fond of stuff like:
// Sort lines
import std.stdio;
import std.array;
import std.algorithm;
void main()
{
stdin
.byLine(KeepTerminator.yes)
.uniq
.map!(a => a.idup)
.array
.sort
.copy(stdout.lockingTextWriter());
Are there any ways to do this that do not involve a bunch of "."s? I do not understand "map!" and "a.idup" either, FWIW.I really want to like D, but it tries to do too many things all at once, in my opinion.
Perhaps I will give C3 a fair try.
vips7L
You don’t like instance functions?
WalterBright
That code is an example of f(a) being equivalent to a.f(). You can do it the f(a) way if you prefer.
`map` is an operation on a data structure that replaces one element with another, in this case `a` gets replaces with `idup(a)`. The `idup` makes a copy of its argument in memory, and marks the data is immutable.
Keyframe
I spent quite a few year on/with it back in the day. There was D1 which was like a better C, and then there was D2 which was like a better C++. Personally I preferred where D1 was going (and Tango instead of Phobos) but even with D2 it really made the day compared to what was out there and to this day still to an extent is. The thing that killed it for me, and I know at least a couple of friends as well (outside of internal politics at a time) was what kills pretty much all exotics once you start using it. Lack of (up-to-date) libraries / bindings and tooling. At the end of the day that's what you do use for most of the work you're doing anyways - libraries. So suddenly you're doing all these bindings and battling tools instead of working on actual problem at hand. This gets tiresome real quick.
For some reason, and mostly that being Mozilla, Rust got quite an initial kick to overcome that initial hurdle in haste. We're not going to mention a lot of those libs are stale in Rust world, but at least they're there and that kind of gives you momentum to go forward. Whatever you're trying to do, there's a non-zero chance there's a library or something out there for you in Rust.. and we got there real quick which then encouraged people to proceed.
That's just like my opinion, man.. but I think a key part is that first lib bindings hurdle which Rust somehow went over real quick for a critical mass of it; D hasn't.
Love the D though lol, and Walter is a 10000x programmer if you ever saw one but it might be time to hang the hat. I can only imagine how a community like Rust or I don't know Zig of those up-and-coming would benefit from his help and insights. He'd probably single-handedly make rust compile 100x faster. One can hope.
jadbox
I've heard nice things about Zig being a more ergo alternative to Rust, but I haven't seen anyone compare it to D yet. From my brief testing, it seemed like Zig wasn't as ergo as D, but in theory it could evolve to maybe get there. From the outside, it doesn't seem like Zig has made any super major ergo improvements in the last year, but I could be wrong.
wavemode
I don't know if I would describe Zig as ergonomic per se. It has some nice features, but the main focus is on completely explicit control over low-level details. In its design Zig always chooses explicitness and programmer control over ergonomics. If a language feature requires a lot of compiler magic then it's probably never going to be added.
mamcx
Stressing the point, Rust ship very early with formatter, linter, cargo, rustup, and was not that behind in terms of editor support.
That is basically table stakes for a new language now.
steveklabnik
I spent time back in the day with D as well, incidentally. I wonder if we crossed paths back then.
Keyframe
for sure we did, Steve! Sometimes multiple times a day even, hah. Check out @keyframe2 on bsky or @keyframe on the evil platform and let's reconnect.
nicoburns
I'd say that the compiler not being open source during the period when it might otherwise have become popular is probably a pretty big factor.
destructionator
The D parts of the compiler were released under the GPL from almost the beginning, since 2002. By 2004, a full open source compiler - what we now call gdc, officially part of gcc - was released using this GPL code. D was pretty popular in these years.
zem
scala is probably the poster child for supporting every paradigm you might want to use :) oz/mozart has more but that was essentially a research/teaching language specifically designed to use a wide range of paradigms in order to demonstrate them.
nickpp
Lack of large “sponsors”.
jadbox
Has anyone compared D and Zig? I originally learned D over one weekend and then went on to completed several code competitions- the ergonomics of D are just fantastic.
azhenley
I really enjoy these lists of interesting features from various languages. They pop up occasionally on HN but now I can’t find them (Hillel Wayne had multiple).
I want a meta list of all these interesting features across languages.
EDIT: I found one! “Micro features I’d like to see in more languages” https://buttondown.com/hillelwayne/archive/microfeatures-id-...
Alifatisk
I like D, it's fascinating and powerful language. It made it even more curious when I watched Tsodings video on D. One thing that came to my mind when reading the article is that things like int.init instead of 0 and $ as shorthand for array.length does add to the mental load.
One good memory I had is a couple of years ago when I built a little forum using D. Man the site was blazing fast, like the interaction was instant. Good times.
WalterBright
The `.init` is there because the default initializer isn't always 0.
burnt-resistor
Invariants, dependent typing, and refinement types FTW.
In Rust land, it really need integration of something like flux into the language or as a gradually-compatible layer.
Can't have safe software without invariant checking, and not just stopping at bounds checking.
mamcx
I don't use D but think that error handling is one major feature:
https://dlang.org/articles/exception-safe.html
In concrete, looks to me to be the only language that covers the major ways to do it.
(In concrete the `scope` way is the one I found inspiring. . I think the exceptions can go and be replace by it for langs where exceptions are removed)
WalterBright
`scope` is very good at its job. It guarantees that a pointer passed as an argument does not escape the caller's scope. I find it almost as useful as transitive `const`.
zzo38computer
In my opinion, some features of D are good, but I do not like all of them.
CTFE is good.
I do not really like the UFCS; if you want it to be used like a member of the first parameter then you should define it as a member of the first parameter (possibly as a inline function that only calls the freestanding function with the same name, if that is what you want it to do). (You could use a macro to do this automatically if you want to.)
Scoped imports is good, but I think that scoped macros would also be helpful.
Exhaustive switch seem like it might be better if designed differently than it is, but the idea seems to be not bad, in general.
LorenDB
UFCS is a bit overreaching but I think it's great for its intended use of chaining expressions on ranges and such.
destructionator
that actually wasn't its intended use; that's a side effect. The original intended use came from Effective C++ by Scott Meyers: "Prefer non-member non-friend functions to member functions.". It was meant to make that as syntactically appealing as the members.
tealpod
D is one of the most beautiful and very efficient language. I wonder why it never got the attention it deserves.
bachmeier
Interesting that there's nothing on there about C interop (likely reflecting the use cases of the author). D does it all: ImportC (compile C code), BetterC (make a D library part of a C program), and easy C interop in both directions.
kwoff
Dlang always makes me think of two things: Walter Bright, resident of Hacker News. And awesome games I played on Linux in the 2000s: https://en.wikipedia.org/wiki/ABA_Games
> Syntax - UFCS (Uniform Function Call Syntax)
UFCS is such an underrated language feature. When you have UFCS you can toss out 90% of the uses of methods in favor of just plain ole functions. Add generic functions and concepts and you rarely end up needing OO support.