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.
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.
>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:
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.
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.
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).
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.
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.
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.
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?
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.
+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.
I think it’s really interesting that as dynamically-typed languages increasingly encourage explicit type hints, statically-typed languages are recommending “almost always auto”.
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.
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?
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.
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):
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.
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.
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.
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.
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.
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.
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"?
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.
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?
The dmd backend itself used -betterC to make the conversion from the original C-with-Classes code. DMC++ itself underwent the conversion to D using -betterC.
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?
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".
> 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?
[+] [-] F-0X|5 years ago|reply
And that's a huge shame. Because in general I really liked using D.
[+] [-] gmueckl|5 years ago|reply
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
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:
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
[+] [-] p0nce|5 years ago|reply
[+] [-] lumberjackstian|5 years ago|reply
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
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
[+] [-] WalterBright|5 years ago|reply
[+] [-] self_awareness|5 years ago|reply
Could you please paste some example of a function that has a return value which is declared as auto?
[+] [-] schveiguy|5 years ago|reply
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
[+] [-] pansa2|5 years ago|reply
[+] [-] arunc|5 years ago|reply
[+] [-] jimbob45|5 years ago|reply
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
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
[+] [-] underthensun|5 years ago|reply
[+] [-] ahartmetz|5 years ago|reply
[+] [-] stevefan1999|5 years ago|reply
[+] [-] dgellow|5 years ago|reply
- "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
[+] [-] optymizer|5 years ago|reply
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
[+] [-] WalterBright|5 years ago|reply
[+] [-] sgt|5 years ago|reply
[+] [-] skohan|5 years ago|reply
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
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
[+] [-] rakoo|5 years ago|reply
[+] [-] arunc|5 years ago|reply
- 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
Just do a template
void zeros(T)(ref T arr) { arr.each!"a = 0"; }
someArr.zeros;
[+] [-] WalterBright|5 years ago|reply
[+] [-] dmux|5 years ago|reply
[+] [-] pjmlp|5 years ago|reply
Then again, I am biased.
[+] [-] colonwqbang|5 years ago|reply
Compiling with GCC -Wall -Werror, the type error is easily caught by the compiler.
[+] [-] kazinator|5 years ago|reply
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
[+] [-] WalterBright|5 years ago|reply
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.
[+] [-] WalterBright|5 years ago|reply
https://github.com/DigitalMars/Compiler/tree/master/dm/src/d...
It still looks a lot like C.
[+] [-] Koshkin|5 years ago|reply
[+] [-] teleforce|5 years ago|reply
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
[+] [-] michaelmior|5 years ago|reply
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?
[+] [-] unknown|5 years ago|reply
[deleted]
[+] [-] runjake|5 years ago|reply
https://thedailywtf.com/articles/The_Secret_to_Better_C
[+] [-] gok|5 years ago|reply
[+] [-] kalium-xyz|5 years ago|reply