6 ms·
What is a good comparison of D relative to other languages? And what are the biggest improvements/nice things it allows?
by hnedeotes 5y ago
What is a good comparison of D relative to other languages? And what are the biggest improvements/nice things it allows?
- mhh__ 5y agoThere are quite a few ways of approaching D - at it's core it's basically designed to have many obvious features that make life easier, rather than being the XYZ language. The underlying ethos is really doing obvious things and making the compiler do the work for you - you can generate huge amounts of fast code at compile time. For example, unittests are a first class keyword in D, which makes writing tests easy, therefore people actually write tests. This feature is maybe a 100 line diff to most compilers, and yet basically only D exploits that fact. Similarly with compile time reflection. You'll have to make your mind up for yourself, but D is proof that the supposed tradeoff between expressiveness and speed is the result of nothing but stockholm syndrome.
- iudqnolq 5y ago> unittests are a first class keyword in D, which makes writing tests easy, therefore people actually write tests. This feature is maybe a 100 line diff to most compilers, and yet basically only D exploits that fact. I'm interested in how this makes the testing experience much easier. In most of the modern languages I've used setting up tests is a similar amount of words (Elixir, Rust) or one-time fifteen minute task (Kotlin). In all of those there are options for tightly-integrated testing environments, but they're not (entirely) literally in the compiler so you can make your own competing one.
- mhh__ 5y agoIt's not about ease at scale but rather friction locally. If you can enforce tests from the top down then all you've saved is some characters, but if you can make testing easier from day 0 people write more tests and get better coverage. There is no setup. Also, the unit tests the compiler implements are just special functions, the actually running of the tests can be as dumb or as complicated as you want (Which D makes trivial because of the reflection capabilities). One thing that I don't think you can do with other approaches, is actually have a unit test inside a template, so you know it gets tested for all parameters that it is instantiated with.
- iudqnolq 5y ago> if you can make testing easier from day 0 people write more tests and get better coverage. I could see it helping move from 0 to nonzero, but most languages have a constant overhead in test setup. If one person starts testing, or you have a standard template, the setup is done. > Also, the unit tests the compiler implements are just special functions, the actually running of the tests can be as dumb or as complicated as you want (Which D makes trivial because of the reflection capabilities). Nice! That's basically the approach rust takes (annotate tests with #[test], either use the default test runner that generates a main fn that runs them or bring your own). > One thing that I don't think you can do with other approaches, is actually have a unit test inside a template, so you know it gets tested for all parameters that it is instantiated with. That does sound useful. You definitely can't do that in any language I've used. If you need that you definitely have buggy abstractions, but then we always do.
- mhh__ 5y ago> If you need that you definitely have buggy abstractions, but then we always do. Not quite, or at least not always. D embraces something called design by introspection - that basically means adapting behaviour at compile time based on what a parameter can do rather than what a parameter should do (in the case of a template). This allows you to write extremely expressive (and fast) code, however you do at this point need to test at a more granular level. https://www.youtube.com/watch?v=HdzwvY8Mo-w https://www.youtube.com/watch?v=HdzwvY8Mo-w
- WalterBright 5y ago> the approach rust takes Quite a lot of D's innovative features have become standard in other languages as well. For example, there was an overlooked feature in Ada that I always liked: int i = 1_000_000; int phone = 214_222_1234; I.e. _ can be inserted in numeric literals to make them easier to read. After D did popularized this, this feature started appearing in other languages. Other such features include ranges, and staticif. Even static assert.
- mhh__ 5y ago
- WalterBright 5y agoHere's what happens (the same thing happened with Ddoc, the documentation generator build in to the compiler). Before Ddoc, there were documentation generators like DOxygen, a third party tool. Doxygen was a fine documentation generator, with lots of great features. The trouble was, it was a third party tool: 1. There were other documentation generators, all different. 2. People would install D, and not have a documentation generator. They'd have to do research, pick one, and install it. 3. The third party tool may not work that well with D. 4. The third party tool may be out of sync with the version of D that the user is doing. 5. The third party tool always comes with a large and intimidating manual. The end result was, maybe 1% of D users used a documentation generator. 10% wrote documentation as a separate file. 89% just wrote zero documentation at all. Even worse, the 10% documentation rarely matched what the function actually did. In fact, sometimes it was unrecognizable. This was the state of, for example, the D runtime library documentation. All of it was either totally wrong, out of date, or missing entirely. Enter Ddoc. Yes, Ddoc is inferior to DOxygen. But every D user now has a documentation already installed, and a perfect match for the version of D they have. Now writing documentation becomes trivial, as Ddoc can read the function headers. When one modifies the function, it's trivial to fix the documentation, as it's right there. When submitting a pull request, the reviewer goes "where's the documentation". The result was a night and day difference. Suddenly the Phobos documentation was all there, and accurate. The same effect happened with builtin unittests. Sure, D's builtin unittest framework isn't as good as advanced third party testing frameworks. But it's always there, and always works, and the difference in results is stunning. Take a look at the Phobos source code - half of it is unittests.
- gavinray 5y agoEDIT: Short summary (half-subjective) = "More ergonomic + flexible higher-level-syntax C++ that performs just as well." -------- I'm a programming language enthusiast and enjoy learning + experimenting with them in my free time. I've written code in most things, especially newer or niche languages. (Always interesting to try them out and get new perspectives) Anyways, I've quickly grown attached to D, even in comparison to say Rust or Nim, and I could maybe speak to some of those points: - C/C++ interop: One of only a handful of languages that can do direct C++ interop without a wrapper (others being Ada, Nim, and experimentally Swift). But it has the most ERGONOMIC interop with C/C++ of any language because of how syntactically similar it is. - Syntax: If someone were to tell me there was a language that felt like you were writing some kind of very high level Javascript/Typescript/Kotlin/C/C++ mixture, but you still had as much low-level power and access as C++, I would have assumed the language was a joke. That's kind of what D is like -- it's not a joke though! - Language constructs: D really hit the nail on the head in my mind here. It has scope guards ("scope(exit)", etc, for methods) void main() { writeln("<html>"); scope(exit) writeln("</html>"); // ... } Compile-time function execution, and a metaprogramming/reflection system that blows away most high-level languages I've worked with. Here's a compile-time generic template that can be added to a struct/class to mix in function pointer definitions from another module or struct/class with reflection: mixin template InjectFunctionPointers(T) { static foreach (fptr; __traits(allMembers, T)) { static if (isFunctionPointer!(__traits(getMember, T, fptr))) { mixin(typeof(__traits(getMember, T, fptr)).stringof, " ", fptr, ";"); } } } struct MyStruct { // Compile-time embedded into the struct with reflection InjectFunctionPointers!(module.with.function.pointers); } And I'm not sure what the exact term for this is, but it's the equivalent of Rust's "where: T" constraints for methods and C++'s concepts (sort of). This shows generics/templates, variadic template arguments, and the concept I just mentioned: auto cartesianProduct(R1, R2, RR...) ( R1 range1, R2 range2, RR otherRanges ) if (!allSatisfy!(isForwardRange, R1, R2, RR) || anySatisfy!(isInfinite, R1, R2, RR)); And it has Design-by-Contract long square_root(long x) in { assert(x >= 0); } out (result) { assert((result * result) <= x && (result+1) * (result+1) > x); } do { return cast(long)std.math.sqrt(cast(real)x); } D has "no-GC" modes and manually memory management if you want. You can just add "@nogc" above a function (or an entire module/program) and it'll disable the garbage collector. There's also "-betterC" for a stripped down, non-memory managed, C-like version of D without the stdlib. // Disable the garbage collector for the method // Or, put @nogc: at the top of a module/program to disable it for the entire thing @nogc void main() { // stuff } There's also a somewhat newer Rust-like ownership + borrow-checker system in D, with @live. https://dlang.org/blog/2019/07/15/ownership-and-borrowing-in-d/ https://dlang.org/blog/2019/07/15/ownership-and-borrowing-in... https://news.ycombinator.com/item?id=20441519 https://news.ycombinator.com/item?id=20441519 Last but not least, the community is small but active and close-knit + welcoming. ------------------- That being said: - D has a much smaller ecosystem of tooling and libs than Rust/C++ - Similarly the development experience isn't quite on par. It's not bad though, quite usable, particularly on VS Code (the Visual Studio extension is also solid).
- Seirdy 5y agoIn addition to what other commenters have said, I'll add that D has good portability. It's able to target operating systems and architectures outside the current mainstream. IMO, its niche is similar to Go and Nim.
- gavinray 5y agoYeah, I cross-compiled from Linux to Windows with LDC and it was literally adding one line to config and using "-triple x86_64-windows-msvc" Pretty mindblowing. Also the dependency management and build system doesn't suck.
- qorrect 5y agoThat is pretty mind-blowing, awesome work there.
- Seirdy 5y agoOther entries in the recent wave of "compiled high-level" languages also neglect support for operating systems outside the mainstream Linux, macOS, and Windows or ARMv8 and x86_64; D supports a variety of operating systems and targets thanks to its alternative compilers: => https://wiki.dlang.org/Compilers https://wiki.dlang.org/Compilers C and Go also fare well in this regard; D sort of fills a niche between them.
- WalterBright 5y agoOne thing that makes porting D code easy is byte, short, int and long have the same size across all platforms. It's unbelievable how much time is wasted in C/C++ programming accounting for int not being a reliable size. I know one can use stdint.h, but people still just use `long` and then the code breaks when ported between OSX and Linux. Another simple but convenient feature is, what if you need the maximum size of an int? #include <limits.h> int max = INT_MAX; But what about: typedef int myint; myint mymax = MYINT_MAX; // oops D has properties, here a .max property: int max = int.max; typedef int myint; myint mymax = myint.max; Nice things like this eliminate a lot of porting bugs.