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.
.init is not necessarily 0. For int it is, but for float it's NaN. For char it's 255 and for an enum, it's whatever you have decided it is.
enum thing {
first = -1,
second,
etc.
}
This way, a variable of type thing has -1 as init value.
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++.
I feel like this feature could be implemented on top of more universal features.
Checking input parameters is easy, just write asserts at the start of the function.
Checking result requires "destructor" block and some kind of accessible result variable, so you can write asserts in this destructor block which you can place at the start of the function, as well.
Checking class invariants requires a way to specify that some function should be called at the end of every public function. I think, it's called aspect-oriented programming in Java and it's actually useful for more things, than just invariant checking. Declarative transaction management, logging.
There are probably two schools of programming language designs. Some put a lot of features into language and other trying to put a minimal number of features into language which are enough to express other features.
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.
> 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.
Plus the chicken and the egg problem. This is mostly from the AerynOS experience : it seems like if you want to write some moderately complicated code then you're becoming the upstream of many libraries. Especially now with Rust's popularity and ecosystem maturity on the rise, it's super hard to convince people (e.g. your boss) that you'd be better of with D compared to e.g. Rust.
Articles like this I guess. If these are the 'lovable' features I'd hate to see the 'meh' features.
Automatic constructors - You only have to write the 'make me a box of two apples' code and not 'this is how two apples go into a box'! This is as revolutionary as 'automatic function calls', where you don't have to manually push the instruction pointer to pop it back off later.
Parenthesis omission!
If I were to parody this I'd talk about how good Scala is - in addition to classes, you can also declare objects, saving you the effort of typing out the static keyword on each member.
Sell me something nice! Millions of threads on a node. Structured concurrency. Hygienic macros. Homoiconicity. Higher-kinded types. Decent type inference. Pure functions. Transactions.
I needs a "Django", "Rails", "ASP .NET" type of project that is out of the box ready. Go has net/http out of the box. I think if D had either a batteries included (Vibe.d is nice, but you have to handle some things still) web framework, it might convince some folk to use it more.
Go spread the way it did because it was dead simple to make websites and services, the syntax is insanely simple, but the language allows you to scale without going through a ton of hoops. Look at how much of our IT infrastructure is powered by Go now.
D has a lot of potential, but someone has to sit down and build frameworks and tooling for D.
I think if D had officially supported libraries by core maintainers that take full advantage of the best of D it might be a different landscape.
Another area where D could shine is GUIs. Everything is electron these days, it feels like nobody builds usable GUI stacks. If D had an official solution to this, and it worked nicely and gave you enough power to customize your UI, we might see a shift there too. I welcome a Electron free future.
Look at the Zed editor (ignore the AI buzz) and how insanely fast it is. Its coded in Rust, and uses WGPU iirc to just render everything kind of like a video game, but it runs insanely fast. It is my new favorite text editor.
Sadly despite my deep love of D, Go is where I'm leaning more towards, due to industry pull there.
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)
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.
> When you have UFCS you can toss out 90% of the uses of methods in favor of just plain ole functions.
Can those go away if you use inheritance or polymorphism, and you need your functions to access protected or private members? I mean, OO is as much about methods as functional programming is about functions.
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.
I'd love to see more of these. In fact, I think it would make an amazing language feature zoo. Mine are heredoc, underscores to format large number 8_098_162_123
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.
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.
Honestly the main reason UFCS is good is when you don't "own" the type your function would need to be a member of. Most common one I run into is the "to" function from the stdlib. You can do this:
import std.conv;
int foo=3;
string bar = foo.to!string
But now lets say you want to convert ints to MyCustomInts? You can hardly attach a new "to" member to int, but with UFCS it's easy. Just declare a to function anywhere in scope:
MyCustomInt to(T)(T v) if(is(T==int)){
return MyCustomInt.from_int_value(v)
// or however you actually do the conversion
}
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.
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
When I last looked at D they did a lot of interesting stuff with compile time function execution and s.th. like dependent types that they hacked into the language.
Can someone give me an update about the current state of this?
Strangely my favourite feature after 18 years of D programming (!) is that could place keywords largely in the order you like. It is strangely liberating to be able to put:
> Invariants are functions that run at the start and end of every public member function, and can be used to ensure that the type is always in a valid state.
The ironic thing about this is that it means that the public functions of an object can't, generally, call each other willy-nilly unless they take special precautions: very often, the object's invariants are broken when the execution is in a middle of the object's method. This is not a problem with D, the language merely helps you to uncover this lurking problem which usually simply goes unnoticed until something breaks and someone notices it.
For me, D failed to replace C++ because of lack of design. It is more a mix of great features. But once you start learning the details simple things can get very complicated.
For example, function arguments can be "in", "out", "inout", "ref", "scope", "return ref" - and combinations.
Another example is conditional compilation. Great when used sparely, but can otherwise make it very difficult to understand how the code flows.
In the end, reading the source code of the standard library convinced me against it.
(The source code for the C++ standard library is much worse, of course).
> function arguments can be "in", "out", "inout", "ref", "scope", "return ref" - and combinations.
None of them are required. What they do is provide enforcement of semantics that otherwise would have to be put in the documentation. And, as we all know, function documentation is always either missing, outdated or just plain wrong.
For a (trivial) example:
void foo(int* p) { *p = 3; }
vs:
void foo(out int i) { i = 3; }
In the latter, you know that `i` is being initialized by the function. In the former, you'll have to rely on the non-existent documentation, or will have to read/understand the foo()'s internals.
I hope some day D gets some WASM story going. Right now the support is very limited, pretty much only through the C subset of D which defeats the point. Supposedly the GC was the main issue, but other GC enabled languages are already thriving in the Web (Go, Java, C#).
There were some efforts to enable D on the web but it seems like these are mostly one man efforts that implement only the minimum features that their specific projects required.
49 comments
[ 2.8 ms ] story [ 77.0 ms ] threadOne 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.
is it not the same as the one in Eiffel?
Checking input parameters is easy, just write asserts at the start of the function.
Checking result requires "destructor" block and some kind of accessible result variable, so you can write asserts in this destructor block which you can place at the start of the function, as well.
Checking class invariants requires a way to specify that some function should be called at the end of every public function. I think, it's called aspect-oriented programming in Java and it's actually useful for more things, than just invariant checking. Declarative transaction management, logging.
There are probably two schools of programming language designs. Some put a lot of features into language and other trying to put a minimal number of features into language which are enough to express other features.
What D or C++26 can do, is a subset of Eiffel capabilities, or more modern approaches like theorem proving in tools like Ada/SPARK, Dafny, FStar,...
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.
Plus the chicken and the egg problem. This is mostly from the AerynOS experience : it seems like if you want to write some moderately complicated code then you're becoming the upstream of many libraries. Especially now with Rust's popularity and ecosystem maturity on the rise, it's super hard to convince people (e.g. your boss) that you'd be better of with D compared to e.g. Rust.
Then came the D2 re-write which broke backwards compatibility and took another few years.
In the meantime everyone moved on
Automatic constructors - You only have to write the 'make me a box of two apples' code and not 'this is how two apples go into a box'! This is as revolutionary as 'automatic function calls', where you don't have to manually push the instruction pointer to pop it back off later.
Parenthesis omission!
If I were to parody this I'd talk about how good Scala is - in addition to classes, you can also declare objects, saving you the effort of typing out the static keyword on each member.
Sell me something nice! Millions of threads on a node. Structured concurrency. Hygienic macros. Homoiconicity. Higher-kinded types. Decent type inference. Pure functions. Transactions.
Go spread the way it did because it was dead simple to make websites and services, the syntax is insanely simple, but the language allows you to scale without going through a ton of hoops. Look at how much of our IT infrastructure is powered by Go now.
D has a lot of potential, but someone has to sit down and build frameworks and tooling for D.
I think if D had officially supported libraries by core maintainers that take full advantage of the best of D it might be a different landscape.
Another area where D could shine is GUIs. Everything is electron these days, it feels like nobody builds usable GUI stacks. If D had an official solution to this, and it worked nicely and gave you enough power to customize your UI, we might see a shift there too. I welcome a Electron free future.
Look at the Zed editor (ignore the AI buzz) and how insanely fast it is. Its coded in Rust, and uses WGPU iirc to just render everything kind of like a video game, but it runs insanely fast. It is my new favorite text editor.
Sadly despite my deep love of D, Go is where I'm leaning more towards, due to industry pull there.
I did not like DUB at all. Its default behavior was to not segregate artifacts by configuration, and trying to change that was a headache.
It’s too bad, though. It’s a nice language, but I can’t see it making any inroads at this point.
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)
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.
Eg. any function call can be converted to a method call on the function's first parameter:
Helps a lot with chaining.Can those go away if you use inheritance or polymorphism, and you need your functions to access protected or private members? I mean, OO is as much about methods as functional programming is about functions.
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-...
> Structs and classes can even overload this operator
nope. fuck, now it's a terrible idea
C# has "^n" notation that means "length - n". For example:
Take the last element of an array or any other structure that supports indices:
Take the last three elements of an array or any other data structure that supports ranges:And that's a very simple example, in Zig, that kind of equations doesn't scale well, when it comes to readability
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.
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.
Can someone give me an update about the current state of this?
The current LSP is _that_ bad, it doesn't even recognize most notable D features such as templates and named arguments..
This should be their #1 priority, as the language is starting to get steam again, they should not miss tat opportunity
I know Walter does not use that kind of tools, but that's becoming a requirement nowadays for young developers
Please, invest into tooling!
(come on, a low effort joke now and then is ok, if not for anything else, as an counter-example)
The ironic thing about this is that it means that the public functions of an object can't, generally, call each other willy-nilly unless they take special precautions: very often, the object's invariants are broken when the execution is in a middle of the object's method. This is not a problem with D, the language merely helps you to uncover this lurking problem which usually simply goes unnoticed until something breaks and someone notices it.
For example, function arguments can be "in", "out", "inout", "ref", "scope", "return ref" - and combinations.
Another example is conditional compilation. Great when used sparely, but can otherwise make it very difficult to understand how the code flows.
In the end, reading the source code of the standard library convinced me against it.
(The source code for the C++ standard library is much worse, of course).
None of them are required. What they do is provide enforcement of semantics that otherwise would have to be put in the documentation. And, as we all know, function documentation is always either missing, outdated or just plain wrong.
For a (trivial) example:
vs: In the latter, you know that `i` is being initialized by the function. In the former, you'll have to rely on the non-existent documentation, or will have to read/understand the foo()'s internals.`out` definitely is a win. Let's look at `scope`:
vs: void foo(scope int* p) { static int* pg = p; } // compiler flags errorThis is most definitely a memory safety feature.
But you prefer C++?
(The D standard library is in the process of being re-engineered for clarity, but still, it is far more comprehensible than C++'s.)
D was so far ahead of C++98 that it wasn't funny, but garbage collection kept it from being able to be used in the same niche.
D has gotten less dependent on garbage collection but
C++11 (and then 17, 20, 26 (reflection)) have closed the gap enough that it is not the quantum leap that it once was.
There were some efforts to enable D on the web but it seems like these are mostly one man efforts that implement only the minimum features that their specific projects required.