I've never seen the D language before and I have to admit, it looks very elegant and has piqued my interest. Thanks for your post.
I'm wondering why you used D rather than Rust? Was it just that you were curious about the D language or is there something about Rust that you don't like?
I see that you mentioned Nim. Have you used it much? I would be interested to hear your thoughts on main features which makes D your favourite over Nim.
I haven't looked at Nim in a while actually (it was still called Nimrod back then). But I want to say right away that Nim is a great language!
I prefer meta-programming in D as I don't get to think at the AST level; its practical in Lisp because the code is the AST, but everywhere else I like compile-time function evaluation better - it feels more natural. It's the same reason why I don't like template-Haskell.
D also has string mixins for the rare occasion when templates reach their limits. You get something akin to "eval" in javascript, but as a compile-time construct. Mixed with the other compile-time features of D (function evaluation, reading files, full reflection and more) it makes for the most powerful thing I know of after Lisp macros. I never have to write offline code analyzers and generators again!
I prefer interfacing with C in D because I don't get to mess with a FFI; I can copy/paste the C definitions in D, run a few Emacs macros and I'm good to go. (Although that's becoming a non-issue over time as more and more bindings are published!)
As far as I know (correct me if I'm wrong!) there's no way to declare pure functions in Nim. This is something I do all the time in D!
Nim has memory-safety only when using the GC while D has decoupled it from the allocation strategy. D's memory-safety is actually part of the type-system (as function annotations) and I absolutely love it!
That's about what comes to mind right now :)
Please note that these points merely makes D a better fit for my own use-cases, I'm also biased from knowing D a whole lot more than Nim, so take what I say with a grain of salt ;)
Interesting points. I personally consider Nim's metaprogramming to be the best there is, although I must admit that I have not tried D's yet. Let me try to give you some reasons why I love Nim (I'll try to touch on all the points you've made about D) :)
Nim includes templates (declarative metaprogramming), and macros (procedural metaprogramming). The latter gives you access to Nim's AST. Macros (and compile-time functions) are evaluated using Nim's VM. The VM supports the full language, with the exception of FFI, but you do get special compile-time functions for reading files and executing external processes at compile-time so it's already very powerful!
Nim compiles to C/C++ so interfacing with C (and C++) could not get easier. Have not tried writing a macro for my editor which converts a C definition into Nim, sounds like a fun thing to implement.
> As far as I know (correct me if I'm wrong!) there's no way to declare pure functions in Nim. This is something I do all the time in D!
> Nim has memory-safety only when using the GC while D has decoupled it from the allocation strategy. D's memory-safety is actually part of the type-system (as function annotations) and I absolutely love it!
Going to need to look into that. Sounds awesome!
In any case, you should definitely give Nim another chance. I can certainly say that I will do the same for D :)
Thank you for taking the time to write this up!
I wasn't aware of the distinction between templates and macros in Nim, it does indeed sound powerful.
I'm also glad to see Nim having purity, its incredibly hard to go back once you've tasted it!
The Emacs macros I mentioned aren't saved anywhere; the similarities in syntax between C and D makes it very straightforward to write dumb macros on-the-fly and forget them afterwards. For the most part D improves on C's syntax by fixing a lot of its shortcomings and the macros merely reflect that.
One example would be to convert "#define foo 1" into "foo = 1," to be put inside an enum declaration. Another would be to remove the DLL_EXPORT references (as D does not require them). The function definitions themselves barely change at all :)
One thing I'm also curious about are compilation times. My D program still compiles under 2-3 seconds even with recursive reflection of 100+ aggregate types, outputting meta-data and code for every single type and field (to handle serialization, generate on-screen editors, resolve dependency graphs and more). I haven't found any other systems language giving me this much power for this little compilation times.
This even includes transforming my regex expressions into D code during compilation so the regexes get the full power of the language's optimizer.
I'll definitely keep Nim in mind for my next pet project, right now I'm getting over 30k LoCs in my D project and really don't want to switch languages halfway through! :)
I'm not the poster, but having tried my hands at both D and rust, I chose D over for my new project at $work. There is far less upfront mental gymnastic with D compared to Rust and that is a nice benefit when you are trying to get things done.
I continue to dabble in Rust, but D is so much more comfortable.
Hey! Yeah D is a really nice language. I have been poking around in Rust, but I don't feel I'm good enough at it to write a blog series with the language.
Thanks for that link! I'm currently writing a compiler for a minuscule language[1] and it's great to be able to look at how other people tackle the same problem and learn.