On 28/11/2017 3:01 AM, A Guy With an Opinion wrote:
Hi,
I've been using D for a personal project for about two weeks now and
just thought I'd share my initial impression just in case it's useful! I
like feedback on things I do, so I just assume others do to. Plus my
opinion is the best on the internet! You will see (hopefully the sarcasm
is obvious otherwise I'll just appear pompous). It would probably be
better if I did a retrospective after my project is completed, but with
life who knows if that will happen. I could lose interest or something
and not finish it. And then you guys wouldn't know my opinion. I can't
allow that.
I'll start off by saying I like the overall experience. I come from a C#
and C++ background with a little bit of C mixed in. For the most part
though, I work with C#, SQL and web technologies on a day to day basis.
I did do a three year stint working with C/C++ (mostly C++), but I never
really enjoyed it much. C++ is overly verbose, overly complicated,
overly littered with poor legacy decisions, and too error prone. C# on
the other hand has for the most part been a delight. The only problem is
I don't find it to be the best when it comes to generative programming.
C# can do some generative programming with it's generics, but for the
most part it's always struck me as more specialized for container types
and to do anything remotely outside of it's purpose takes a fair bit of
cleverness. I'm sick of being clever in that aspect.
So here are some impressions good and bad:
+ Porting straight C# seems pretty straight forward. Even some of the
.NET framework, like files and unicode, have fairly direct counterparts
in D.
+ D code so far is pushing me towards more "flat" code (for a lack of a
better way to phrase it) and so far that has helped tremendously when it
comes to readability. C# kind is the opposite. With it's namespace ->
class -> method coupled with lock, using, etc...you tend to do a lot of
nesting. You are generally 3 '{' in before any true logic even begins.
Then couple that with try/catch, IDisposable/using, locking, and then
if/else, it can get quite chaotic very easily. So right away, I saw my
C# code actually appear more readable when I translated it and I think
it has to do with the flatness. I'm not sure if that opinion will hold
when I delve into 'static if' a little more, but so far my uses of it
haven't really dampened that opinion.
+ Visual D. It might be that I had poor expectations of it, because I
read D's tooling was poor on the internet (and nothing is ever wrong on
the internet), however, the combination of Visual D and DMD actually
exceeded my expectations. I've been quite happy with it. It was
relatively easy to set up and worked as I would expect it to work. It
lets me debug, add breakpoints, and does the basic syntax highlighting I
would expect. It could have a few other features, but for a project that
is not corporate backed, it was really above what I could have asked for.
+ So far, compiling is fast. And from what I hear it will stay fast. A
big motivator. The one commercial C++ project I worked on was a beast
and could take an hour+ to compile if you needed to compile something
fundamental. C# is fairly fast, so I've grown accustomed to not having
to go to the bathroom, get a drink, etc...before returning to find out
I'm on the linking step. I'm used to if it doesn't take less than ten
seconds (probably less) then I prep myself for an error to deal with. I
want this to remain.
- Some of the errors from DMD are a little strange. I don't want to crap
on this too much, because for the most part it's fine. However
occasionally it throws errors I still can't really work out why THAT is
the error it gave me. Some of you may have saw my question in the
"Learn" forum about not knowing to use static in an embedded class, but
the error was the following:
Error: 'this' is only defined in non-static member functions
I'd say the errors so far are above some of the cryptic stuff C++ can
throw at you (however, I haven't delved that deeply into D templates
yet, so don't hold me to this yet), but in terms of quality I'd put it
somewhere between C# and C++ in quality. With C# being the ideal.
+ The standard library so far is really good. Nullable worked as I
thought it should. I just guessed a few of the methods based on what I
had seen at that point and got it right. So it appears consistent and
intuitive. I also like the fact I can peek at the code and understand it
by just reading it. Unlike with C++ where I still don't know how some of
the stuff is *really* implemented. The STL almost seems like it's
written in a completely different language than the stuff it enables.
For instance, I figured out how to do packages by seeing it in Phobos.
- ...however, where are all of the collections? No Queue? No Stack? No
HashTable? I've read that it's not a big focus because some of the built
in stuff *can* behave like those things. The C# project I'm porting
utilizes queues and a specifically C#'s Dictionary<> quite a bit, so I'm
not looking forward to having to hand roll my own or use something that
aren't fundamentally them. This is definitely the biggest negative I've
come across. I want a queue, not something that *can* behave as a queue.
I definitely expected more from a language that is this old.
Its on our TODO list.
Allocators need to come out of experimental and some form of RC before
we tackle it again.
In the mean time https://github.com/economicmodeling/containers is
pretty good.
+ Packages and 'public import'. I really think it's useful to forward
imports/using statements. It kind of packages everything that is
required to use that thing in your namespace/package together. So you
don't have to include a dozen things. C and C++ can do this with it's
#includes, but in an unsatisfactory way. At least in my opinion.
- Modules. I like modules better than #include, but I don't like them
better than C#'s namespaces. Specifically I don't like how there is this
gravity that kind of pulls me to associate a module with a file. It
appears you don't have to, because I can do the package thing, but
whenever I try to do things outside that one idiom I end up in a soup of
errors. I'm sure I'm just not use to it, but so far it's been a little
dissatisfying. Sometimes I want where it is physically on my file system
to be different from how I include it in other source files. To me, C#'s
namespaces are really the standard to beat or meet.
Modules are a fairly well understood concept from the ML family.
You are not use to it is all :)
Keep in mind we do have namespaces for binding to c++ code and I haven't
heard of anybody abusing it for the purpose of using name spaces. They
tend to be ugly hacks with ambiguity running through them. Of course I
never had to use them in c++ so I'm sure somebody can give you some war
stories with them ;)
+ Unit tests. Finally built in unit tests. Enough said here. If the lack
of collections was the biggest negative, this is the biggest positive. I
would like to enable them at build time if possible though.
I keep saying it, if you don't have unit tests built in, you don't care
about code quality!
- Attributes. I had another post in the Learn forum about attributes
which was unfortunate. At first I was excited because it seems like on
the surface it would help me write better code, but it gets a little
tedious and tiresome to have to remember to decorate code with them. It
seems like most of them should have been the defaults. I would have
preferred if the compiler helped me and reminded me. I asked if there
was a way to enforce them globally, which I guess there is, but I guess
there's also not a way to turn some of them off afterwards. A bit
unfortunate. But at least I can see some solutions to this.
You don't need to bother with them for most code :)
- The defaults for primitives seem off. They seem to encourage errors. I
don't think that is the best design decision even if it encourages the
errors to be caught as quickly as possible. I think the better decision
would be to not have the errors occur. When I asked about this, there
seemed to be a disassociation between the spec and the implementation.
The spec says a declaration should error if not explicitly set, but the
implementation just initializes them to something that is likely to
error. Like NaN for floats which I would have thought would have been 0
based on prior experiences with other languages.
Doesn't mean the other languages are right either.
- Immutable. I'm not sure I fully understand it. On the surface it
seemed like const but transitive. I tried having a method return an
immutable value, but when I used it in my unit test I got some weird
errors about objects not being able to return immutable (I forget the
exact error...apologies). I refactored to use const, and it all worked
as I expected, but I don't get why the immutable didn't work. I was
returning a value type, so I don't see why passing in
assert(object.errorCount == 0) would have triggered errors. But it did.
I have a set of classes that keep track of snapshots of specific counts
that seems like a perfect fit for immutable (because I don't want those
'snapshots' to change...like ever), but I kept getting errors trying to
use it like const. The type string seems to be an immutable(char[])
which works exactly the way I was expecting, and I haven't ran into
problems, so I'm not sure what the problem was. I'm just more confused
knowing that string works, but what I did didn't.
+- Unicode support is good. Although I think D's string type should have
probably been utf16 by default. Especially considering the utf module
states:
"UTF character support is restricted to '\u0000' <= character <=
'\U0010FFFF'."
Seems like the natural fit for me. Plus for the vast majority of use
cases I am pretty guaranteed a char = codepoint. Not the biggest issue
in the world and maybe I'm just being overly critical here.
That uses a lot of memory UTF-16 instead of UTF-8. I would argue for
UTF-32 instead of 16.
If you need a wstring, use a wstring!
Be aware Microsoft is alone in thinking that UTF-16 was awesome.
Everybody else standardized on UTF-8 for Unicode.
+ Templates seem powerful. I've only fiddled thus far, but I don't think
I've quite comprehended their usefulness yet. It will probably take me
some time to figure out how to wield them effectively. One thing I
accidentally stumbled upon that I liked was that I could simulate
inheritance in structs with them, by using the mixin keyword. That was
cool, and I'm not even sure if that is what they were really meant to
enable.
And that is where we use alias this instead. Do wish it was fully
implemented though (multiple).
Welcome!