D at 20: Hits and Misses [pdf]
digitalmars.com
digitalmars.com
The feature bloat makes it very hard to learn D, let alone being an expert on it. I think it’s telling why its creator focuses on individual features to assess its success. Now, all those missed features either have to be maintained or carefully removed which is a lot of work and might actually break existing code.
I like D because of its vision: C++ for humans. It fails on that vision simply because it has become a gigantic incoherent product like a research project.
It probably needed a harsher approach to feature requests to make sure the language stayed on the vision. Now it’s a programmer’s dream and a programmer’s nightmare at the same time.
I think Go, for instance, has made a better decision to stay barebones.
I see that the creator intends to include Rust’s borrow-checking semantics which reminds me that Rust is probably what D should have been in all aspects.
Not at all. For example, if you know C-style programming, you can be productive in D with a pretty trivial investment in time.
> Rust is probably what D should have been in all aspects
While D will likely get some sort of borrow checking, it will not adopt the look and feel of Rust.
Maybe giving those circumstances a bit more thought would be instructive and useful to you. Maybe not. Good luck!
Learning 100% of any programming language should not be a goal. I know less than 10% of the functionalities of my microwave oven or my washing machine. This doesn't prevent me from successfully using them and save a lot of time.
Bjarne Stroustrup himself says learning all the nitty-gritty details of C++ is counterproductive and unnecessary.
Consider for instance Lua, which is often read by people that have superficial knowledge of the language. Lua has a very standard syntax that anyone with basic general programming knowledge will understand:
> player:set_pos({x=0, y=0})
But it also has syntactic sugar:
> player:set_pos{x=0, y=0} -- note the braces instead of parens
This is where you lose half of your readers because they have to look for that weird syntax in the manual.
In defense of language implementers, it is sometimes difficult to "resist" feature requests. It's difficult to say "no" in general, in particular when you are not Niklaus Wirth and have half a dozen languages designs on your resumé; all you have is your gut feelings as justification.
When diving into new codebases, the couple of esoteric-used-features-you-did-not-know-existed in C++ was never a big deal. Learning the required C++ features was always a matter of days (because the codebase is full of real examples).
Understanding the overall structure of the code, the interactions/dependencies between the components, in what file/directory should go your first feature... this is the hard part. And it's many orders of magnitude harder than learning a couple of new language features.
Don't get me wrong, I'd really like that C++ and D be smaller, and I don't like feature bloat. I once wrote a source code processor based on clang, and then you realize how big the language really is (there are many AST node types), and having all your code written in it starts to feel like technical debt.
However, I pragmatically have to recognize that feature bloat isn't a real problem for professional C++ programmers (It might be, for compiler vendors - at this point, I'm not expecting any new C++ compiler to be created).
PS1: C++ certainly gives you some shiny tools to make interesting messes (I've seen codebases where simply including a header would change the program behavior). This isn't related to the feature count, but rather to the semantic of the individual features themselves, like macros/templates. At least we don't have monkey patching ...
PS2: About your Lua example: even without knowing the syntax, can you sincerely have the slightest doubt about what "player:set_pos{x=0, y=0}" actually does?
I suppose you aren't using any maths either because of the existance of semisimple lie algebras and topological quantum fields ?
If a language feature exists and you program in that language you have to know how to deal with that feature, even if you don't write code using it you have to be able to re-factor it and re-write code that used it.
Unless we become the kind of person that doesn't ever work in teams or we just hate our team mates and care little for project success.
The point the parent makes is a legitimate one. It can be argued against but your response hasn't achieved that in a meaningful way.
If D tried to humanize C++, which is not a given, Rust tried and succeeded to dehumanize it into something that can be analyzed by a simple tool.
a) Keep all the ownership and lifetime information in your head and make sure it gets communicated to other team members. Then write tests to make sure nothing was missed.
b) Formally specify ownership and lifetime so it can be checked by the compiler.
I'm not sure (a) is any less dehumanizing than (b)
I stand by my original statement though:
a) in a language like C and C++ one has to use their knowledge to come up with a solution and verify it. Pretty human.
b) in a GC language, one mostly does not care. When they do, they do a).
c) in Rust one does their best at a) and then does what the robot tells them to, even if the code is correct but the robot can't yet figure it out.
For example, I wondered why in the slides he felt implementing contract programming in D was a miss; it seemed like a strong selling point for the language. According to the talk (around 1:34:30), however, he felt that contract programming was relatively unpopular/unused in D and that "assert" covered most of the use cases for it.
One example:
fun date(y:i32, m:i32, d:i32)
{... code ...}
This scream for more context (and better param names!). With contracts: fun date(y:i32, m:i32, d:i32)
assert
y in 0..9999
m in 1..12
d in 1..31
{... code ...}
But I need to see it in the docs, in the tooltips, etc. Now are useful!A buggy assertion would be one that fires when the input is correct.
Assertions aren’t formal verifiers or replacements for a test suite.
And/or better param (sub-)types.
Those underscores turn out to be a really big deal, and leaving them out of C++ was a massive mistake. Nobody wants to read constants like 00110100101101011011011101011110, but 00110100_10110101_10110111_01011110 is fine. An implementation of binary constants that doesn't allow this is going to be less popular.
0b00110100'10110101'10110111'01011110
It's not ideal, but using underscores was off the table, because things like _100_ are actually valid variable names and it would have caused parsing issues.Of course, it's not as if '0' wouldn't be ambiguous, so I don't really buy the underscore-as-variable-name argument. C++ left context-free grammar behind a long time ago...
reg = (0b110 << 23) ||
(0b0101 << 10);
is by far the clearest way I know to write that. And since hardware descriptions usually (but not always) give descriptions in binary rather than decimal, it's the easiest form to check against the datasheet or manual.Some improvements have been made lately, but not at the level of manpower and CS background from other GC enabled languages.
I am quite confident that if D had something like Microsoft behind it, its GC would long be as good as Sing# or System C# (M#) were.
The underlying issue is that to make the GC more performant requires the addition of "write gates", where writing through a pointer notifies the GC that the memory object has changed. This is a reasonable choice when a language is heavily GC dependent (like Java).
However, D is not at all heavily GC dependent. But it is very dependent on memory references. Hence the GC speedup from adding write gates would be more than lost from the cost of executing those write gates.
The situation could definitely be improved by a lot; d's GC is just plain bad right now. But looking at other languages' GCs may give a misleading impression because those GCs' designs are not suitable for d.
The path pursed in the past by Mesa/Cedar, Modula-3, Sing# or more recently System C#.
Provide automatic memory management by default, while offering all the tooling stack and global allocation, with low level stuff and untracked references requiring explicitly unsafe code.
For more modern approaches that combine those ideas with Cyclone ones, Chapel, Swift, Ada/SPARK, ongoing Haskell and OCaml support for linear types.
So you get the productivity of automatically memory management, graphs, self referencial structures and what not, while being able to go low level for more fine-grained resource management.