That said I really tried to use D for my projects, but I had to give up for a rather surprising reason: the C interop is so good, that 1) most libraries provide a 1-1 translation of their C APIs, which ends up being ugly, non-idiomatic D that forces me to think in both C and D when coding; and 2) debuggers are not aware of D types and idioms, so when debugging, I have to again think in both C and D. Both of those add up to about 90% of the coding time, which is to say that, 90% of the time, when using D, I felt I had to code in 2 languages at the same time.
I'll skip some of the other issues I ran into, because I think a lot of the problems with D would go away if it had a large active community that would put the work in to maintain the D ecosystem, but that's a bit of a chicken and egg problem.
In the end, I decided that for me the reduced language overhead, solid ecosystem and modern conveniences of gnu17 C were more valuable in practice than the sweet features that D had to offer, and that made me a little sad, but I'm hopeful that one day D will make a strong comeback.
I know Walter gets notifications when D is mentioned on HN, and I imagine that if he read through this he'd shake his fist at me for saying interop-so-good-its-bad, but, if I could make a parallel with Java, I'd say that in code that uses many 3rd party libraries, D feels a bit like coding with JNI all the time (sorry). Ironically, in my opinion, D would benefit from having a community that rewrote popular libraries, instead of primarily relying on C interop.
a(b(c(d(e),3))
? Not very readable. UFCS (Universal Function Call Syntax) enables it to be written as: e.d.c(3).b.a();
Reading it flows naturally left-to-right. e.d.c(3).b.a
which is even cleaner.For those who are thinking UFCS is a trivial detail, consider that the shell and some other languages have pipe operators (|>) to make the code flow intuitively the same way as the data.
In my opinion, in a C-like language, managing to squeeze so much functionality out of the '.' operator without any downsides is the mark of a well thought out, elegant language.
Thank you for creating D.
For example, in C++, a = b can invoke anything. Not sure it is a good idea (except for generic code, there it is useful).
Zig has a philosophy of nothing hidden that I think it is mostly good.
That said, I find D a very nice language, the only problems are:
1. small ecosystem 2. last time I tried, packaging of download and use was... improvable.
Naively, I would think that making template instantiation look the same as a function call would be a desirable feature, with ambiguous calls needing to be resolved by the user.
templ!thing(a,b)
I would have thought that templ(thing)(a,b) would have been a good solution, as it is what is used in the declaration/definition side of templates, but this would have made removing redundant () not possible in UFCS expressions. templ<thing>(a, b)
because with that: templ!thing(a, b)
is it a function?, or are you calling thing's function?you can do:
templ!(thing)(a, b)
but did you mean?: templ!(thing(a, b))()
i personally always use !(), no matter what, and it's annoying to type, i don't want to waste time constantly trying to figure out what is what, it's mentally drainingkotlin became very useful for focusing on being able to consume Java code
it allowed them to have a huge presence on android, that's enabler
it profits Zig as well
not everything needs to be ranked #1 in TIOBE index
there is value in being the way it is, it's organic, and no companies get to control its faith
> the C interop is so good, that 1) most libraries provide a 1-1 translation of their C APIs, which ends up being ugly, non-idiomatic D that forces me to think in both C and D when coding;
what do you mean? it's the same, function and data
struct Data {}
do_this(&myData);
this is valid D, it's also valid Cthe problem i think you have is you are abusing OOP and think it's the only way of doing things, which is wrong, and this explain the sad state of software nowadays ;)
but it's weird when you then say you decided to stick with C, you contradict with yourself
> ... when you then say you decided to stick with C, you contradict with yourself
Figments of your imagination.
However, I've run into lots of bugs in Zig so I got a little disillusioned... how do you think D compares with Zig? Is it able to produce as efficient and small binaries, cross-compile to most platforms, metaprogramming etc?
While i like Zig, it has many ergonomics issues that i just can't deal with.. and it has the tendency to make your code unnecessary verbose
- no operator overloading for your math type, you end up chaining methods..
- no function overloading, you have to do things like 'add(comptime T: type, x: antype, y: anytype)`
then: add(f32, myX, myY);
then be prepared with a shit ton of @floatCast(), it can't guess what type you use so you forced to be doubly explicit..
you end up with code that is barely readable
- unused as error, this one hurts a lot.. you can say goodbye to fast iteration time, if you make a game it'll be painful
Zig is still great, i plan to use it for some project, knowing how to use more language helps train your skills and understand pros/cons of features better
You can crosscompile with D too
ldc2 -mtriple=x86_64-windows-msvc -c foo.d
Metaprogramming is great, you get more power with D, mixin, proper templates, type introspectionYou can link without the runtime if size is a concern, i haven't had any issues with it
- float default is NaN, why... just WHY!!!!!
- char default is 0xFF....
when you expect things to be just 0, it is a pain to deal with and to remember...
Also the standard library, while useful, i wish it would make use of allocators
They have ``std.experimental.allocator`` packages, for some reason it still is experimental..
There is no tagged union.. you have to import a package for it ``std.sumtype``.. wich is bad when all other languages have built in support for that
Walter if you read this, please! union are useful, but lack of tagged union mean potential bugs in your union when you pick the wrong value...
Proposal for you:
enum MyTag { A, B, C}
union MyTaggedUnion: MyTag
{
DataA A,
DataB B,
DataC C
}
struct DataA{}
struct DataB{}
struct DataC{}
auto tgu = MyTaggedUnion.B;
switch (tgu)
{
case A:
tgu. /* implicit DataA */
break;
// need to implement every tags, or error
}> char default is 0xFF....
Invalid values, perhaps? Other than making initialization mandatory, this seems like a reasonable way of catching them. For integer types it's not really possible because all their values are valid.
import std.sumtype;
void main(){
struct A { int a; }
struct B { string b; }
struct C { bool c; }
alias TaggedUnion = SumType!(A, B, C);
auto tgu = TaggedUnion(B("hi"));
int i = tgu.match!(
(A a) => a.a,
(B b) => b.b.length,
(C c) => c.c * 5
);
assert(i == 2);
}
If you miss out a `match` handler for any of A, B, C you get a compile-time error. Or you can use a generic handler which will be instantiated for any type not explicitly handled.
https://dlang.org/phobos/std_sumtype.htmlI know of sumtype, and i think it is a mistake for D to rely on this for such important language feature
I'm not a language dev, so i can't do much to help, using switch/union/enum is more natural, is cleaner, and is easier to add support for IDEs, everyone on the same page
That's in areas like this where the language falls short, and i can see people instead choosing alternatives
https://github.com/dlang/phobos/blob/master/std/sumtype.d
a mess of templates and imports to std.traits
using this will make your compile speed tank.. another reason to avoid
it's very sad that people recommend this instead of asking for built-in version, makes me sometimes reconsider my choice to use the language
if they expect language improvements to be templates, what atrocity will be added next?
this is on the same level as std::bool from C++
> The good news is, I have not put a lot of effort so far into micro-optimizing the compile-time performance of match, so there is almost certainly room for improvement.
it is shame that it ended up in the standard library
once Walter will be gone, i will have no faith in the language anymore
All operations that are fed NaN as an operand produce a NaN result. This makes it very obvious when the initialization of a float has been neglected. Defaulting to 0.0 means uninitialization bugs are nearly impossible to detect.
> char default is 0xFF....
Same thing. It's intended to flush out uninitialization bugs.
Disclaimer, I've never used Zig, so the following are not comparative.
> Is it able to produce as efficient and small binaries,
There are compiler flags to disable linking to the D standard library and D runtime if you need small binaries (you can still use templates that end up in you binary) though most of the time that is more of a loss than gain (in terms of productivity), its not bloated unless you abuse templates a lot as would happen in C++. As for speed of execution, don't use DMD if you care about the it. LDC (the LLVM D Compiler) and GDC (which is part of the GCC) are both strong optimisers.
cross-compile to most platforms,
Yes, use LDC. GDC, like GCC, is not a cross compiler by default though you can build it to be, DMD is X86(64) only.
> metaprogramming etc?
Oh, yes. Compile time function evaluation (referred to usually as CTFE) in combination with mixins (interpret string as code), `static foreach`, `static if` and (much safer than C++) templates makes for a very potent package.
How is it a "scripting language" if you don't want a GC? I use D for the vast majority of my scripting these days, but it would be odd for me to avoid the GC. I would not even think about using it if I had to deal with that.
You can certainly use the std without relying on GC in languages like Rust, Swift, etc, but the important thing is that you can’t in D. I’m sure it can be technically done, but nobody has actually put the effort to do it.
Once finished, everything in the std will be able to make use of it
If you can't wait, you can use this package already with the allocators: https://github.com/dlang-community/containers
Note that many game engines do use garbage collection for this purpose
GC only exist if you call ``new MyThing``
Just use your allocators with malloc/free, that is what i do (you do the same in zig, allocators)
if you want to enforce no GC use in your APIs, there is the @nogc attribute you can use
If you do that, is it RAII like C++/Rust? Objects get destructed at end of scope, and members get destructed recursively? Or do you have to manually call `free()` on everything? (which, granted, is significantly easier in languages with `defer` mechanisms)
scope(exit):
scope(failure):
scope(success):
https://tour.dlang.org/tour/en/gems/scope-guardsYou can do RAII with D too, it's your choice, based on your use case
We really need to get actual reference counting into the language to cover classes properly as well.
It has been on my todo list to write a DIP for this for a while now. ~rikki.
scope obj = new Object;
...
// destroyed at end of scopeAnd he made a video game called Empire. I wonder if his character name is Heisenberg....
Being able to just import your C code, and call it from C, or when you need to go back to C and resume your work by just importing your D modules
I tried it, while it still need some finish, it worked very well! (you had to put your files through a preprocessor though, hopefully this step won't be needed in the future)
Every modern language should include a C compiler, it is stupid to give up the entire C ecosystem!
No wonder Walter's name is "Bright"!
To the people who still doubt about D "because of the GC", it doesn't exist for me, i am making an online 3D game targeting WASM without it, with all the language features!
For cases when you need one "if you use D as a scripting language", it will be very helpful to have!
https://www.kdom.xyz/
If you love C, but need something modern with more safeties, give D a try!
D is a pragmatic language, very powerful, covers everyone's needs!
If you tried it before, now is the time to give it another try!