Thread overview
Appreciating D
Jul 13
IchorDev
Jul 14
IchorDev
July 13

This is going to be a long one.

I have been thinking about this blog post a lot recently. It documents how the author—now presumed dead—first used D in 2019 as a replacement to C++ for programming video-games. They discuss how they didn't like the experience of using D, and why they chose to port their code to Jai, an unfinished language which is not available to the general public. They neglect to mention the future implications of the language's unfinished unreleased state for libraryavailability, however.

In the present day it's only nearly two years later, Jai has still not materialised a public release despite its seemingly avid community, and around half of the problems with D mentioned in the post have already been solved. I think this is a great indication of the strength of D's community. Despite some recent roadblocks in getting new features into the language, it has still evolved significantly over the course of a mere two years. I'd like to now go over some of the issues the author had with D at the time and review them for the sake of appreciating D's growth, and analysing some of their misconceptions about the language.

>

After bouncing between the two available D compilers for Windows (dmd and ldc2) for about 4 years, I find the state of D on Windows to resemble something I would expect from a hobby project, [...]
From the following problems that I have encountered over the years, by far the biggest one is that debug info on Windows is completely broken:
[...]
I was told that “DMD has historically had a lot of issues with [...] debug info” [...] but unfortunately most of [these issues] apply to both compilers, not just dmd.

First of all, GDC is also available for Windows, at least in some capacity.
I cannot speak for the quality of D's compilers on Windows (since I have banned it from my house), however I can say that since they experienced issues with LDC2 I suspect that a lot of their issues with debugging could stem from Visual Studio, or how it interfaces with whichever debugger it's using. We're never informed whether it was VSCode or the full VS IDE though, so the answer remains nebulous.

>
  • mixins (D’s macro equivalent) generate debug info in a way that causes the debugger to not find the correct file (so you step through disassembly)

The -mixin flag has always solved this problem for me, and I don't see why it wouldn't work in Windows.

>

[...] there are other issues and shortcomings, to a significant part in metaprogramming:

  • different compiler phases interact in weird ways that lead to surprises in metaprogramming, while generating misleading errors [...] like order independent declaration in global scopes sometimes breaking with mixins

I feel that this is to be expected, and this behaviour can be easily logically identified with a bit of knowledge. A compiler is not magical, it cannot know things it has not gone out and discovered yet. Usually D doesn't require forward declaration because it does a pass that alleviates the need for it, but without giving mixins their own pass, they naturally result in sequentially parsed code. D's backend design has a strong preference for shorter compile times, and adding another pass would be a huge detriment to compile times.
D compilers definitely have an error message problem, though. Since this post was written many previously confusing error messages (especially for missing semicolons & closing parenthesis) have gotten a lot better, but you'll always get insane error messages from the depths of a fever dream when the compiler encounters a fringe error (or possibly even a bug) inside of a 7-layer nested mixin from a CTFE lambda inside an instantiation of an aliased template.

>

or like namespaced names not resolving in mixins unless you use a special kind of string literal

This is a very strange one. First of all, D does not really use 'namespaces', instead we use have scopes. What I assume they mean is fully qualified identifiers, but I cannot find any evidence for anything like this in the history of the D specification for mixins or token strings. The other possibility is that they're using D's 'namespace' variant of extern(C++) (which I do not recommend—use the string version instead), since they mention C++ interoperability. Perhaps one of those was once an implementation detail, but it's also a patently illogical idea. Mixins parse strings into a regular D expressions/declarations, so why would they ever have a special case like this?

>

or like static foreach not being able to insert multiple else ifs after an if

Here we see a fundamental misunderstanding of D's meta-programming. The body of a static foreach inlines a series of Declaration Statements. None of D's meta-programming facilities allow you to write incomplete declarations/expressions the way that C's macros do, because of the disastrous effects this has had on C's readability. If you want to write incomplete declarations like having if and else if declared with meta-programming, use string concatenation with a mixin:

void main(){
	int n;
	import std.writeln;
	mixin((string[] list){
		string ret = "if(n == "~list[0]~"){\nwriteln(`"~list[0]~"`);\n";
		foreach(item; list[1..$]){
			ret ~= "}else if(n == "~item~"){\nwriteln(`"~item~"`);\n";
		}
		return ret ~ "}";
	}(["1", "2", "3"]));
}
>

ldc2 is awfully slow to compile:
It takes 1 minute in debug and over 5 minutes in release mode to compile the game. There’s also the issue of ldc2 somehow needing 8GB of RAM in debug and 11.5GB in release mode for compiling a 20MB executable from 2MB of source files, thus causing regular near-death experiences for my laptop.

When hearing about compile times this slow, and using this much memory, the first thing that comes to mind is severe template abuse, which is a compile time bottleneck I've experienced with LDC2 before, especially when building for release. What's the solution? First of all, acknowledge that whenever you use meta-programming, you're making the compiler run that code, so if that code takes a long time to execute then that will necessarily inflate your compile times. No compiler design can fix that. A big part of the issue for me was re-instantiating template functions in nested static foreach loops. Instead, I would recommend using CTFE functions that create one big mixin.

>

but [ldc2 is] sometimes the only choice because dmd has bugs:
I’ve encountered a couple of bugs over the years that stopped my program from building, the latest one being this codegen bug when interfacing with C++.

The bug mentioned has been fixed for some time now, and importantly, was introduced due to an error in Microsoft's own documentation about the calling convention.

>

D offers a betterC mode that among other things disables garbage collection. However, when using this mode, the standard library does not compile and meta programming is significantly hampered:
Disabling the garbage collector not only disables it for your compiled code, but also for the code executing at compile time. One major way of metaprogramming in D is injecting new code into the program. That code is a string, and those strings need to be concatenated, and string concatenation uses the garbage collector. So … it just doesn’t compile.

There is quite the errata in this section. Specifically, BetterC replaces DRuntime with a wrapper that hooks into C's runtime, which makes some basic DRuntime-dependant features like assert still work. The GC is part of DRuntime, so out it goes. Phobos—D's standard library—is partially unavailable, with most meta-programming facilities (like std.traits/std.meta) still being available.
You might've noticed a discrepancy there. Why would removing the garbage collector from the runtime stop GC being used at compile-time? The compiler's interpreter isn't using the same runtime as our generated run-time code, right? No, of course it's not. D compilers don't even use the GC at compile-time by default, although it can be enabled for lower memory consumption. What this person has clearly encountered is that run-time functions that use the GC/DRuntime naturally don't compile with BetterC (or produce linker errors) and so they have naturally concluded that CTFE string concatenation is impossible with BetterC. This is simply wrong. When you write a run-time function (whether or not you use it in run-time code) the compiler will try to generate code for it. There's a way to make CTFE-only code-paths in a function, but this feature only works with if, not static if, so it's not useful to us here. What you need to do is create a function that the compiler won't try to generate run-time code for. You need a lambda:

//not a lambda, will generate run-time code:
string catRT(string a, string b) => a ~ b; //Error: array concatenation of expression `a ~ b` requires the GC which is not available with -betterC

//a lambda assigned to an enum:
enum catCT = (string a, string b) => a ~ b; //OK

Of course, if you call this lambda in run-time code it will produce an error (from linking, specifically).

>

Bad debug info and the de-facto need for garbage collection are dealbreakers

Garbage collection being a deal-breaker is weird to me as a D user, but somewhat understandable for real-time speed-critical applications. However, D is a perfectly capable language without DRuntime, as you can easily write a custom templated 'array' struct that overloads ~ and uses existing C/C++ libraries instead of Phobos. If you don't care about executable size, just mix GC with manual memory allocation. You'll get to use a lot more D libraries that way. Unless you pick a good allocator or swap a lot of large long-term GC allocations to manual ones (to reduce GC heap scanning) your net time savings will likely be ~0 anyway.

See this excellent write-up about the matter from Bit Bashing for more: Garbage Collection for Systems Programmers

>

Why is there a single language without the ability to pass parameters by name in 2022??

The DIP for named parameters was accepted back in 2020, so anyone could've seen it coming, but now we've finally got our hands on them! Hurrah!

And that's all! If you like long blog posts about D I recommend reading some from Bit Bashing, and The Art of Machinery.

July 13
Thanks for taking the time to write an interesting analysis!
July 14

Good post, but there is one issue: GDC is NOT available for windows in any capacity. It apparently got removed from winlibs a while back, for whatever reason, and is still removed to this day.

July 14

On Sunday, 14 July 2024 at 13:38:27 UTC, Ruby The Roobster wrote:

>

Good post, but there is one issue: GDC is NOT available for windows in any capacity. It apparently got removed from winlibs a while back, for whatever reason, and is still removed to this day.

Oh, thanks for the correction! Their website mentioned D so I was inclined to believe it, but I guess it would hardly be the first time I see a website for a FOSS project that’s incredibly out of date. (For example: GDC’s own website)

July 15

On Saturday, 13 July 2024 at 16:33:25 UTC, IchorDev wrote:

>

This is going to be a long one.

I have been thinking about this blog post a lot recently. It documents how the author—now presumed dead—first used D in 2019 as a replacement to C++ for programming video-games. They discuss how they didn't like the experience of using D, and why they chose to port their code to Jai, an unfinished language which is not available to the general public. They neglect to mention the future implications of the language's unfinished unreleased state for libraryavailability, however.

[...]

Thanks for the summary