top | item 23005297

Better C – A subset of D Programming Language

249 points| dgellow | 5 years ago |dlang.org

352 comments

order
[+] F-0X|5 years ago|reply
One thing that really annoyed me about D is that its documentation lists basically every function with an 'auto' return type. Auto should be completely banned from documentation, it's a complete hindrance. It's the single biggest contributor to why I stopped using D for personal projects - I was so tired of having to hunt down what functions are going to return, sometimes having to resort to just using it and looking at what the compiler complains about.

And that's a huge shame. Because in general I really liked using D.

[+] gmueckl|5 years ago|reply
The use of Auto is requires in some places because the standard library returns types that cannot be named in the context of the calling function. This happens for example with algortihms that return a custom Range implementation that is declared within the scope of the function implementing the algorithm.

I am not sure what to make of this pattern. At least the documentation should be more explicit about these Voldemort types. Documentation has other issues as well. The standard documentation generator doesn't cope well with version statements (conditional compilation), potentially skipping docs for stuff that wouldn't be compiled into a particular build variant.

[+] logicchains|5 years ago|reply
>so tired of having to hunt down what functions are going to return, sometimes having to resort to just using it

With highly generic functions, it's often not possible to know what they'll return without knowing what you'll call them with. Especially given that D functions like "map" and "reduce" tend to return special iterator types so that the compiler is able to smarty fuse them where possible. If D had concepts like C++20, you could probably describe them with something looking like:

    template<class R>
    
      concept __SimpleView =                         //     exposition only
        ranges::view<R> && ranges::range<const R> &&
        std::same_as<std::ranges::iterator_t<R>, std::ranges::iterator_t<const R>> &&
        std::same_as<std::ranges::sentinel_t<R>, std::ranges::sentinel_t<const R>>;
But at least for me that doesn't seem like it would be much more helpful than just reading the documentation, which states what the function returns, if not necessarily the type.
[+] WalterBright|5 years ago|reply
You don't have to wait for the compiler to complain. Using:

    pragma(msg, T);
where T is any type will print the type to the screen during compilation. pragma(msg) will print all kinds of things, making it a very handy tool to visualize what is happening while compiling. I use it all the time.
[+] p0nce|5 years ago|reply
It's often because these functions have unnamed types. Chain of lazy computations in D often return unnamed types (so called "Voldemort" types) because finding good names for those inner structs is a challenge, they have a single use (which is to have a particular signature).
[+] lumberjackstian|5 years ago|reply
I've felt this as well, been using D for a couple years now, and this is the kind of thing that just makes me have to context switch more than I'd like. With the current implementation of the language it's hard to avoid, and function's return type can be quite complex, so writing it down can be hard.

Another reason is the (ironically) dynamic nature of a return type. E.g.

auto whatDoesItReturn(int i)() { static if (i == 0) { return int.init; } else { return string.init; } }

Template code can do that quite easily and then you don't have a choice but to write auto as the return value.

What would be fantastic if the documentation could be given access to the the compiler's return type inference, so that it could document the auto returns with a little more info.

Another way useful approach would be to implement protocols like in swift, or traits like in scala/rust/others, signatures in ml, etc. Then you would be able to define the interface of what a function returns.

[+] skocznymroczny|5 years ago|reply
Personally I dislike var/auto in languages because I like having types explicitly written. But in case of languages like Java or Kotlin you can move the cursor over the variable name and you will see the type, also you can right-click and select "replace with explicit type" and it will work. In D, IDEs struggle with templates and can rarely index templated code (no wonder, because most of the code doesn't exist until build time).

Most people will tell you, "oh just use auto, it makes the code more generic". That's sweet, except as soon as I want to pass it to another function, I need to have the concrete type. Like you, I usually just copy-paste the full type from the error message and move on.

[+] acehreli|5 years ago|reply
I've been using D since 2009 both personally and professionally. 'auto' return did bother me in a few places but it never came close to being a deal breaker.
[+] WalterBright|5 years ago|reply
It's helpful to bring up issues you are encountering in the D forums. This is the first I've heard of this particular one.
[+] self_awareness|5 years ago|reply
I've never programmed in D so I don't know, but from curiosity I wanted to check if what you write is true. However, I can't find any function that is declared as auto.

Could you please paste some example of a function that has a return value which is declared as auto?

[+] schveiguy|5 years ago|reply
Sometimes I feel like auto is definitely overused. In a recent fix, I changed something that returned a boolean (no templates involved) from returning auto to returning bool.

But sometimes auto is the best tool for the job, especially when writing wrapping types. In that case, yes, you have to read the documentation (and I mean what is written in the ddoc comments). But in many cases, you don't have to, because you recognize the pattern, or it's simply a wrap of the underlying type's function.

[+] dirtydroog|5 years ago|reply
+1, I cringed when Herb Sutter released the 'Almost Always Auto' C++ presentation on YouTube. Sure, auto has its place, and I personally use it, but I just knew that less experience devs would go nuts with it, and it'd only make their lives easier for a short time.
[+] pansa2|5 years ago|reply
I think it’s really interesting that as dynamically-typed languages increasingly encourage explicit type hints, statically-typed languages are recommending “almost always auto”.
[+] arunc|5 years ago|reply
Vim vs emacs, auto inference vs explicit, I don't think it will ever end. :)
[+] jimbob45|5 years ago|reply
I vehemently disagree. Auto/var is a tool that may be used judiciously by your userbase. This new philosophy of blocking your users from using dangerous tools because you know better than them just invites workarounds and kludges. Give the user the tools and warn them of the ways it can go wrong. There's a reason Rust is losing the war to C++.

Anyways, var/auto is critical in some cases. C#'s LINQ, for example, would be very difficult to develop with if you had to manually figure out the type you were returning with long queries every time you wanted to restructure your query.

[+] JoeAltmaier|5 years ago|reply
More a failure of documentation/tools? We've been content for a decade to just name the arguments and return without any context. Like the old joke "Where am I?" answered by "In a hot air balloon!". Correct but useless.

I wonder if the document could describe (in some regular way) how those auto types are constructed...from what input, with what operations?

[+] z3t4|5 years ago|reply
Im annoyed that you have to spell out auto all over the place. It should be implicit. The compiler should automatically add auto if you have not specified something else.
[+] underthensun|5 years ago|reply
I really hate the auto keyword but as I like D so much, I kinda get used to it.
[+] ahartmetz|5 years ago|reply
Ugh, that is even more ill-advised than using auto without good reason in C++.
[+] stevefan1999|5 years ago|reply
Well, not everyone likes automatic type deduction -- some people just like to torment him/herself by repeating information that is unneeded. Why?
[+] dgellow|5 years ago|reply
For more context and details, Walter Bright wrote a series of blog article between 2017 and 2018 on the subject (though their content is a bit outdated as more D features are now supported in BetterC mode):

- "D as a Better C" (2017): https://dlang.org/blog/2017/08/23/d-as-a-better-c/

- "Vanquish Forever These Bugs That Blasted Your Kingdom" (2018): https://dlang.org/blog/2018/02/07/vanquish-forever-these-bug...

- "DasBetterC: Converting make.c to D" (2018): https://dlang.org/blog/2018/06/11/dasbetterc-converting-make...

[+] kqr|5 years ago|reply
This is a great idea. I maintain that Ada is a better "better C" than any of the alternatives I've looked into, but it has an obvious big hurdle: while it has approximately the same use cases as C, it is completely different in terms of looks and handling. One of the strong points of D is that it still seems very much like C. Very good call to emphasise this.
[+] optymizer|5 years ago|reply
Mr. Walter Bright, I'm late to the party here, but I hope you will see my comment.

I have a lot of respect for you and Andrei, and I think D deserves much more love than it gets.

I personally feel like there are too many options when it comes to D. Perhaps it would be good to double down on some combination of those options and make that widely known?

As a newcomer to D, I am exposed to GC/non-GC code, dmd/llvm compiler/gcc compiler, several debuggers, some BetterC option, etc. I don't know which toolchain is best, and I don't want to deal with the integration issues between them.

All of these options that exist are great, but the brand gets diluted. D seems to be the ideal thing: it's seemingly a better C++, and a better C, with a clean syntax like Java.

I trust it's a better C, but how do I know the combination of the D toolchain that I'm choosing is better than sticking with the devil I know already?

What I would love to see is an opinionated package spun off from D. One compiler, one debugger, one IDE, one standard library. Make my onboarding experience better and take the thinking out of assembling a D toolchain.

Every time I find myself thinking "maybe I should look into using D instead of C for this project", I spend a few hours with D, and then I get frustrated at all the options and I go back to the devil I know.

I truly wish I could be one day convinced that I can install D and I get a solid platform without weird moving parts.

[+] dgellow|5 years ago|reply
Yes, I completely agree, the onboarding experience is daunting. It's quite hard to understand all the moving parts and figure out which one I should care about or not.
[+] WalterBright|5 years ago|reply
If you were to pick one option, stick with BetterC.
[+] sgt|5 years ago|reply
D is pretty exciting. Seems like a perfect choice for those of us looking for an alternative systems programming language and are not completely convinced we'll be happy in Rust.
[+] skohan|5 years ago|reply
Yeah I think "better C" is a space which has a really good reason to exist and I'm always interested to see new entrants.

IMO describing Rust as a "C replacement" is slightly off the mark because Rust's value proposition is very different from C. Rust is about giving you the best possible performance in a safe-by-default language. C is about giving you maximal control over memory, with a very thin layer of abstraction over the hardware.

C has traditionally been used in a lot of places where Rust's USPs add a lot of value -- for instance in kernel development -- simply because there wasn't a safe alternative. However I think there are other cases where the strengths of C still add value; for instance in game development you're largely trying to do high-throughput processing over large swaths of structured data, and Rust concepts which help safety like RAII just get in your way. Yes there are ways to get around this in Rust, but Rust is not really optimized for structured, manual memory management.

As a result I think there is plenty of room for something like C which just has better ergonomics and some more modern features.

[+] pjmlp|5 years ago|reply
D is a much better option for those that believe that it isn't a crime to have a GC on a systems programming language.

It basically follows on the school of thought at Xerox PARC, ETHZ, and Microsoft Research.

Now what it lacks is more more manpower to improve its runtime capabilities and having a big name actually pushing it forward.

However this doesn't need to be a zero sum game, any language that helps to fix C is welcomed to the party, including attempts like Checked C and Frama-C.

[+] pron|5 years ago|reply
Mine is Zig.
[+] rakoo|5 years ago|reply
I'm not looking for an alternative to C but I used this quarantine to start nim, and it feels quite right until now
[+] arunc|5 years ago|reply
For me the single best feature that stands out in D is the uniform member access using dot.

- Want to access member in a aggregate (class/struct)? Use .

- Want to access member in pointer to an aggregate? Use .

- Want to access member in a module? Use .

- Want to access reference (not free standing)? Use .

This makes switching between different implementations pretty easy. Coupled with UFCS, this is pretty fantastic!

[+] jane_red|5 years ago|reply
I can strongly relate to that. You can write a custom "fill" method for your array and just do "arrray.fill", bam! It just works. You want numpy "zeros"?

Just do a template

void zeros(T)(ref T arr) { arr.each!"a = 0"; }

someArr.zeros;

[+] pjmlp|5 years ago|reply
Now with WebAssembly support thanks to LDC, the Better C experience is definitely much more productive than dealing with emscripten.

Then again, I am biased.

[+] colonwqbang|5 years ago|reply
The chosen example (printf) doesn't make D look very good compared to C. If I modify the example to include a simple type error:

    printf("Hello %s", 123);
Then the D version will still happily compile without warnings and will segfault.

Compiling with GCC -Wall -Werror, the type error is easily caught by the compiler.

[+] kazinator|5 years ago|reply
The problem is that since this Better C is not compatible with any C dialects, and requires a D compiler, there is no reason to restrict D programs to this subset. At least no reason that is related to simply working in something better than C. The real reasons can be articulated better, starting with a different name, like "Reduced D" (D that avoids garbage collection, exceptions and/or has a smaller footprint or whatever). How about "Dm": D Minor: a sad subset of D.

A subset of C++ is a better C, that can be written in such a way that C compilers translate it, so that is meaningful. Well, at least a slightly better C, anyway.

[+] zem|5 years ago|reply
every time I've looked into this I've found the documentation severely lacking in examples of how one would go about incrementally migrating a c project to better c. does someone have a pointer to a blog post or even a git commit that illustrates the first step of migrating a single c file, with all the attendant makefile changes?
[+] WalterBright|5 years ago|reply
1. rename the file from prog.c to prog.d

2. set up to compile with dmc prog.c -c -betterC

3. replace all the preprocessor stuff

4. compile it, and fix the errors diagnosed by the compiler

The hardest part will be how much metaprogramming was done using the C preprocessor. Once that's dealt with, the rest is fairly mechanical.

[+] Koshkin|5 years ago|reply
Regarding the ongoing auto/var readability issues debate, how about the popular dynamic languages that often do not even have the means of specifying a type? They surely must be suffering from huge readability issues?
[+] teleforce|5 years ago|reply
This is a nice tutorial introduction on using D as a better replacement for C [1].

Some excerpts "At one time, C was my go-to language for most programming. One day I realised that most of my C programs kept reimplementing things from C++: dynamic arrays, better strings, polymorphic classes, etc".

[1]https://theartofmachinery.com/2019/04/05/d_as_c_replacement....

[+] hirves_lot|5 years ago|reply
I think D is great for scripting.. and having no python experience but java/c/c++ it feels more familiar. The standard library is useful.
[+] michaelmior|5 years ago|reply
> To link D functions and libraries into C programs, it's necessary to only require the C runtime library to be linked in.

Not that I'm suggesting this would be a better approach, but wouldn't it be possible to link both the C and D runtime libraries and wrap the main function to do the initialization?

[+] gok|5 years ago|reply
Feels like a more accurate name would be "Freestanding D" but it's a cool idea.