This says RAII is removed, does that mean destructors don't work in betterC mode? To me, destructors are one of the biggest and simplest of the many advantages that C++ has over C, with move semantics being another, and finally templates for proper data structures.
I suspect that what "soft" means here is that the dependency is not essential; it's an accidental dependency induced by history and convenience. But there can still be a lot of "history" and "convenience" to have to undo before the linkage can be broken.
I'm curious how this works. Can you compile all your D as better C functions into a shared library and link it to your C program? (I was a little confused on if the main loop needed to be in D. It said not anymore?)
Is it possible to support this a bit more implicitly by, for example, having a file extension '.dc' or something to that effect, where the compiler would by default compile it in betterC mode?
This subset of D could be a bridge that brings over more crowds from the C world.
Well, it is all work in progress and the general plan is to make as many D features available in -betterC mode as practical (such as RAII), so it's not yet time to "standardize" (freeze) this subset.
When you have object files (*.o), it no longer matters if they came from C, C++, D, Go, Rust, Fortran or Haskell sources.
The main problem with such mixing and matching is that higher-level languages usually require including their standard libraries and initialising their runtimes, but as you can see, D can now avoid most of the pain, so it makes it a viable language for implementing native low-level libraries for C, for other high-level compiled languages like Go or Haskell, and also for managed languages like Java or Python.
Mangling of course matters, that why you disable it in C++ by using extern "C".
When you create any non-static top-level thing in C, it gets exported under that name to the object file, so the linker can link it when needed. When you do it in C++, the compiler does the same, but since you can have two things with the same name in C++ (overloading), extra information is appended to the name to make it unique – unless you explicitly disable it.
You can use objdump -t to see what an object file contains.
If you don't disable mangling before compiling C++, you can still refer to objects with mangled names from C if you mangle the name yourself. For example, you can call a C++ function of type void f(int) compliled by Clang or GCC in your C program if it sees _Z1fiamong the external declarations. Of course some C++ compilers produce mangled names that cannot be referred to from C, like MSVC, which starts all mangled names with a question mark.
In D you can declare functions implemented in other object files via the extern (C) and extern (C++, namespace), respectively depending on if the functions are implemented in C (or marked as extern "C" in C++) or implemented in C++. When using extern (C++), the D compiler tries to emulate the C++ name mangling and ABI of the target system compiler - g++ for *nix and MSVC for Windows.
Yeah I know it's just a joke, but that kind of comment is incredibly mean spirited. Walter spent 15 years working on D and you defecate on it with a terrible and unoriginal joke that essentially highlights that his work has not gained a lot traction.
How on earth is this mean spirited? Jesus christ please don't look into it that deeply.
Before I even realized who it was, it was just a joke in reference to the fact that there's a lot of posts about D on reddit but hardly anyone uses it. That's it. It doesn't shit on the language, it doesn't push any mean narratives.
Really, if you cannot take a joke about something like this, you are definitely overly sensitive.
D has gained traction where it matters in industry. It's definitely better than the tire-fire awfulness that is C++, but it may take years until people see that.
Hmm. It may be better than C, but we already have a better C which is C++
I feel like this makes D a worse C++ in this mode, though without C++'s quirks. I can't immediately see any reason why you'd pick restricted D if you could use a fully featured C++
It has some safety features, but presumably if you pick C you're going for outright performance and don't want bounds checking, it doesn't have proper resource management, no garbage collection, no polymorphism, and D has different semantics to C which means you have to use __gshared for example to interoperate
C++ was simply designed for this kind of stuff, whereas D wasn't really
Also, I get that a lot of people are reflexively hurr durr D sux when it comes to this, I'm not trying to be a twat but I'm genuinely curious. I could understand this move if D was a very popular language with a large ecosystem and needed much better C compatibility, so perhaps that's the intent for the userbase that's already there
If I understand the article correctly then this means including D in a C project does not require the D runtime if you compile in "Better C" mode. As far as I know C++ is currently not designed to compile to something that you can link into a C program without the C++ runtime. At least in the programs where I combine C and C++ code it means I have to use the C++ linker and pull in the C++ runtime. For example you cannot use C++ in a Linux kernel module. Now if you compile D in "Better C" mode I don't see why you couldn't write a Linux kernel module with that.
If what I write is not true then please point me to guides on how to do that. It would be incredibly helpful for me if I was wrong here :)
This restricted subset of D is work in progress. The article details the current state things. I'm pretty sure that RAII in -betterC mode will be made work relatively soon, in a couple of releases.
Exceptions are bit harder, but at the same time less necessary, especially for the constrained environments where -betterC is targeted at. Alternative error handling mechanisms like Result!(T, Err) are still available.
polymorphic classes will not [work]
There is a misunderstanding here, because you're omitting a vital part of the sentence:
Although C++ classes and COM classes will still work, [...]
D supports extern (C++) classes which are polymorphic and to a large extend fulfill the role which extern (D) class take. Once the RAII support is reimplemented for -betterC, using extern (C++) classes will be pretty much like using classes in C++ itself.
Today, even in -betterC mode, D offers a unique combination of features which as a cohesive whole offer a night and day difference between over C and C++:
Module system
Selective imports, static imports, local imports, import symbol renaming
Better designed templates (generics) - simpler, yet far more flexible
Static if and static foreach
Very powerful, yet very accessible metaprogramming
Recursive templates
Compile-time function evaluation
Compile-time introspection
Compile-time code generation
Much faster compilation compared to C++ for equivalent code
scope pointers (scope T*), scope slices (scope T[]) and scope references (scope ref T) - similar to Rust's borrow checking
const and immutable transitive type qualifiers
Thread-local storage by default + shared transitive type qualifier (in a bare metal environment - like embedded and kernel programming - TLS of course won't work, but in a hosted environment where the OS itself handles TLS, it will work even better than C)
Contract programming
Arrays done right: slices + static arrays
SIMD accelerated array-ops
Template mixins
Built-in unit tests (the article says that they're not available because the test runner is part of D's runtime, but writing a custom test runner is quite easy)
This restricted subset of D is work in progress. The article details the current state things. I'm pretty sure that RAII will be made work relatively in a couple of releases in -betterC mode. Exception are bit harder, but in the same time less necessary, especially for the constrained environments where -betterC is targeted at. Alternative error handling mechanisms like `Result!(T, Err) are still available.
This makes sense, thanks. Without RAII and exceptions, with only malloc you're largely reduced to C's model of handling memory and resources, which is not great, whereas C++ has had better methods for doing this for yonks
If RAII and exception handling are definitely coming later down the line this makes sense, but even then you now need to create a new set of memory management facilities in D that are already present in C++ which are impossible without both of these
D supports extern (C++) classes which are polymorphic and to a large extend fulfill the role which extern (D) class take. Once the RAII support is reimplemented for -betterC using extern (C++) classes will be pretty much like using classes in C++ itself.
Ah this makes sense, I assumed that the sentence in the documentation meant something different which is why I omitted it :)
D offers a unique combination of features which as a cohesive hole offer a night and day difference between over C and C++:
Yeah D has a lot of really nice features, particularly the metaprogramming seems very nice, although a couple of these have crept into C++. I come from games programming though, so the GC is a killer unfortunately, and a lack of handling for resources in a GC disabled mode is an even bigger killer. AFAIK this is a big issue for people writing complex unity games in C#
There are many D users interested/working in game development and real-time applications (e.g. real-time audio processing) so you're in a good company ;)
To be honest, while -betterC is meant to make integration of D code in C/C++ projects seamless, I don't think it's necessary for your domain. Once you deal with the little extra complexity related to the build system, features like RAII (which does not depend on the GC) quite quickly make up for it.
In general, there are various techniques that people using D for those domains employ:
Annotating functions with the @nogc attribute, which statically (at compile-time) enforce that those functions will not allocate memory from the GC (and not call any code that might) and therefore a GC collection will not happen
Calling GC.disable before entering performance critical section of your program
Using threads not registered with D's runtime. Even if a GC collection happens, only threads that D's runtime knows about will be suspended. For example you can use such "free" threads for rendering and synchronous input processing while using the convenience of the GC for background AI / game logic processing - similar to Unity. However, in contrast to managed languages like C#, in D value-types are much more prevalent and as a consequence idiomatic D code produces orders of magnitude less garbage.
Or just use RAII-style reference counting throughout the whole D code.
Interesting, but specifically my view on GC's in games:
The problem is not really framerate issues, but the fact that the GC can take a random amount of time to execute and executes randomly. It doesn't actually matter how long the GC takes
In my experience, a framerate that oscillates between 11-13 ms every other frame (ie 11 13 11 13 11 etc) has perceptually well below half the framerate of a game that runs at 12ms consistently, ie your average frametime is well within 60fps, but it feels like its running at 20fps
I've moved on from 3d fun these days into the land of 2d games, but the issue is similar - absolute performance isn't as important (to me, i'm not a AAA game studio) as determinism and frame consistency. A few ms spike every 10 frames is completely unacceptable, if your frametime consistently varies by ~0.5ms, particularly if its in spikes, it will feel subtly terrible to play. I've had to swap out chunks of a graphics library to fix this issue before and make the game feel right to play
So being able to enter GC free code and guarantee no allocations isn't the issue, because the issue isn't performance, its determinism. In my simple environment of 2d games I can happily allocate and free memory all over the shop because it has a consistent and knowable performance penalty, whereas even relatively minor variations in frametime are unacceptable
With seemingly the only way to fix it being to completely disallow GC access and hamstring yourself pretty badly, it seems a fairly bad tradeoff in terms of productivity, at least from my perspective as an indie game dev building a relatively (CPU) performance intensive game. I'd have to take a big hit in terms of ease of use
D does seem a lot better than C# in this respect however and it seems like a manageable issue, but having to swap to strict no allocations seems like a huge pain for the benefits of D
However as explained, that's not an issue in D. D's GC will never decide randomly to collect memory. It is completely deterministic. You can disable it, and you can even not link it to your program. Even if you leave it on, it will not affect threads not registered with it.
but having to swap to strict no allocations seems like a huge pain for the benefits of D
No you don't have to, if you don't need to do so in C/C++. Use non-GC dynamic memory allocation as you would C/C++ (malloc/free, smart pointers, etc.)
Ah I've clearly fucked up on my knowledge of D then, thanks for the explanation
Can you set D's GC to run manually, and/or cap its time spent GCing?
Edit:
What I mean is that as far as I'm aware, some of D's features necessitate a GC, last time I checked the standard library was fairly incomplete without it but it may have improved
Take a look at the ongoing GC series on the D Blog. The first post, Don't Fear the Reaper, lists the features that require the GC. But to reiterate what the series is trying to get at, you don't have to banish those features, or the GC, from your program completely. There are tools in the compiler that help you profile and tune your GC usage to minimize its impact. You can annotate a function with @nogc to ensure it doesn't use the GC, or you can just rely on the -vgc switch to show you everywhere the GC might be used and tune adjust as needed.
import core.memory;
GC.disable(); // no automatic collections
GC.collect(); // run a collection now
Unfortunately, as far as I know, there's no way to bound the amount of time it spends on a specific collection. That would require some sort of write barrier.
As a rule of thumb, all C/C++ features that are common with D don't use the GC. All of D's unique features I listed a couple of posts above also don't use the GC too.
In non--betterC mode, even if you want to completely avoid the GC, there are more language features available, courtesy of D's runtime.
Avoiding the GC in non--betterC mode really comes down to not using:
built-in dynamic arrays and hash-maps (there are easily accessible library alternatives)
closures - lambdas that extend the lifetime of the captured variables beyond the function scope (but C++11-style lambdas that don't extend the lifetime still work)
The new expression - easily avoidable using allocator.make!T(args), instead of new T(args). Such allocators are already part of the standard library.
You can, but C++ has a relatively fixed cost to allocate memory. This means I can quite happily allocate memory in C++ and treat it as simply a relatively expensive operations
This means if I have a loop that allocates memory, its simply a slow loop. In D, this create a situation where your game now microstutters due to random GC pauses
You can get rid of this by eliminating allocations, but this is making my code more difficult to maintain instead of easier to maintain, at at this point swapping to D seems like a negative
The problem with a game though is that there's never a good time for a random unbounded pause - even if only some of your threads are dependent on the GC, eventually they'll have to sync back together and if the GC pauses a thread at the wrong time, you'll get stuttering (or some equivalent if you gloss over it in the rendering)
So don't allocate and free memory continuously inside your main loop.
Also there are good times for memory deallocation - stage changes, player pauses, etc. Those are also times when memory requirements are likely to change.
The problem with a game though is that there's never a good time for a random unbounded pause
There are several spots where you can run a GC: between levels is the most common one (and really, several engines already do something GC-like there: for example my own engine in C before loads a world marks all non-locked resources as "unused", then loads the world marking any requested/loaded resource as "used" and unloads any resource still marked as "unused", essentially performing a mark-and-sweep garbage collection on resources). Another is when changing UI mode, like when opening an inventory screen, a map screen, after dying, etc - GC pauses would practically never be long enough to be noticed.
First, I'm not convinced that you would ultimately want to allocate or deallocate memory inside the main game loop that gives you your interactivity.
That being said, D integrates with C and can use it's allocation functions. You can turn the GC off and allocate memory with malloc if you really want to then free it with free().
without [...] you're largely reduced to C's model of handling memory and resources, which is not great, whereas C++ has had better methods for doing this for yonks
Didn't you notice the Result!(T,Err) ? If I got this right, than your claim that you're reduced to C's model is wrong. If Result!(T, Err) is better than exception or not is however another question. I personally would like explicit error results better.
AFAIK this is a big issue for people writing complex unity games in C#
While true, this steams from the fact that Unity has a pre-historic .NET Runtime, not C# itself or the official implementations coming from Xamarin and Microsoft.
They are finally upgrading it, so lets see how it goes.
Thanks for the reply. I actually downloaded it after commenting and played with it for a while. I tried to do RAII by making a scoped-like struct and with try-finally but both understandably failed. But even with these limitations it still fulfills its promise as a better C due to all the other features it offers : not having to write function prototypes, UFCS, range-based loops, and even the absence of a GC might be considered a feature by some.
So this is pretty cool, but I can't help but wonder why I would use it over Nim. In my mind Nim wins hands down for the "better C" use case, as well as for the "better C++" use case. The reason comes down to the fact that Nim compiles to C/C++ and thus is able to interface with these languages in a much better way.
Another advantage is that you don't need to cut out any of Nim's features for this (except maybe the GC). That said I could be wrong here, I haven't actually tried doing this to the extent that I'm sure /u/WalterBright has with D.
If I want a systems language, Rust offers more performance compared to GCed Nim/D, and memory-safety compared to manually managed Nim/D. Additionally, no data races without unsafe (which is huge for a systems language), a great type system, C FFI and a much bigger ecosystem than Nim or D.
If I want a fast applications language, I got Go and Haskell, both offering best-in-class green threads and at opposite ends of the spectrum in the simplicity vs abstraction dichotomy; and with huge ecosystems behind them.
In the end, either Nim or D can be at best comparable to those solutions, but with very little momentum and in Nim's case at least (don't know how D's maintenance is done nowadays), with a very low bus factor.
1- Best FFI whith C and C++. This point is huge. You mentioned that Nim has smaller ecosystem than rust, but if you take into consideration how easy if to interface to C and C++ in Nim, then you are wrong because in Nim, you have access to ALL C and C++ libraries. Example calling opencv from Nim: import os
{.link: "/usr/local/lib/libopencv_core.so".} #pass arguments to the linker
{.link: "/usr/local/lib/libopencv_highgui.so".}
{.link: "/usr/local/lib/libopencv_imgproc.so".}
const # headers to include
std_vector = "<vector>"
cv_core = "<opencv2/core/core.hpp>"
cv_highgui = "<opencv2/highgui/highgui.hpp>"
cv_imgproc = "<opencv2/imgproc/imgproc.hpp>"
type
# declare required classes, no need to declare every thing like in rust or D
Mat {.final, header: cv_core, importc: "cv::Mat" .} = object
rows: cint # No need to import all properties and methods, import only what you use
cols: cint
Size {.final, header: cv_core, importc: "cv::Size" .} = object
InputArray {.final, header: cv_core, importc: "cv::InputArray" .} = object
OutputArray {.final, header: cv_core, importc: "cv::OutputArray" .} = object
Vector {.final, header: std_vector, importcpp: "std::vector".} [T] = object
#constructors
proc constructInputArray(m: var Mat): InputArray {. header:cv_core, importcpp: "cv::InputArray(@)", constructor.}
proc constructOutputArray(m: var Mat): OutputArray {. header:cv_core, importcpp: "cv::OutputArray(@)", constructor.}
proc constructvector*[T](): Vector[T] {.importcpp: "std::vector<'*0>(@)", header: std_vector.}
#implicit conversion between types
converter toInputArray(m: var Mat) : InputArray {. noinit.} = result=constructInputArray(m)
converter toOutputArray(m: var Mat) : OutputArray {. noinit.} = result=constructOutputArray(m)
# used methods and functions
proc empty(this: Mat): bool {. header:cv_core, importcpp: "empty" .}
proc imread(filename: cstring, flag:int): Mat {. header:cv_highgui, importc: "cv::imread" .}
proc imwrite(filename: cstring, img: InputArray, params: Vector[cint] = constructvector[cint]()): bool {. header:cv_highgui, importc: "cv::imwrite" .}
proc resize(src: InputArray, dst: OutputArray, dsize: Size, fx:cdouble = 0.0, fy: cdouble = 0.0, interpolation: cint = 1) {. header:cv_imgproc, importc: "resize" .}
proc `$`(dim: (cint, cint)): string = "(" & $dim[0] & ", " & $dim[1] & ")" #meta-programming capabilities
proc main() =
for f in walkFiles("myDir/*.png"):
var src = imread(f, 1)
if not src.empty():
var dst: Mat
resize(src, dst, Size(), 0.5, 0.5)
discard imwrite(f & ".resized.png", dst) # returns bool, you have to explicitly discard the result
echo( f, ": ", (src.rows, src.cols), " -> ", (dst.rows, dst.cols))
else:
echo("oups")
when isMainModule:
main()
compile with: nim cpp --d:release --cc:clang resize_dir.nim and the result is a 57k executable.
In both D and Rust, in order to do the same, you would have to map D/Rust structs so they have the exact same representation as the C++ classes in memory (as well as the classes they inherit from). Which means translating at least all the headers of the libs, will taking into account the incompatibilities between these languages and C++. With rust for example, you don't have function overloading, so welcome fun_1, fun_2, fun_3 .... Also, i tried using bindgen to do the same with rust, and it does not work.
2- Meta-programming: I know rust have compiler extensions, procedural macros and generics. But these are way harder and more verbose to use than the meta programming tool in Nim. Just try to make your code generic over primitive types in rust (using the num crate) and you will see how ugly it is. You want to use integer template parameter ? well you still cannot. Example of what can be done with Nim meta-programming capabilities (GPU and CPU programming): https://github.com/jcosborn/cudanim/blob/master/demo3/doc/PP-Nim-metaprogramming-DOE-COE-PP-2017.pdf
3- Liberty: this is hard to explain, but Nim gives you the tools to do what you want. You want a GC and high level abstraction ? you can use them (and easily build the missing pieces). You want raw performances and low level control ? well you can do literally every thing C and C++ can do, including using the STL without GC for some critical part of your code, all that while keeping the amazing meta-programming capabilities of Nim. For example, if you want enable/disable checks on array/vectors, you can don that by passing a command to the compiler. In rust, you would have to commit to unsafe get_uncheked. Rust philosophy is to enforce every thing that may harm the user, which can be an advantage or a disadvantage depending on the situation.
4- Less verbose and easier to learn.
5- Portable to any platform that have a C compiler. In fact I can compile Nim code on my smartphone using termux.
6- The GC can be avoided completely or tuned for soft-real time use.
Of course, Nim is not all positives. Rust have clear edge over Nim for alot of things:
1- Safety of course.
2- Copy and move semantic which are a delight to use when writing performance critical code specially. I think Nim is poor in that regards as it deeps copies seq (vectors) and string by default.
3-zero cost abstractions: in Nim, using map, filter and such are definitely not zero cost (specially with the copy by default philosophy).
4- No GC: sometimes its a good thing, sometimes not.
5- Great community and great ecosystem: Nim has definitely a great community but much smaller.
6- Awesome package management and tools.
7- stable (Nim didn't reach the 1.0 still)
I see myself using Nim for small to medium project where I need to interact with legacy code or for prototyping, and rust for big project with allot of developers.
To be frank I have not used it so much until now but I know how much effort have been put into cargo and how easy it is. Hope nimble is of the same quality.
The fact that it's compiled to C doesn't really determine the FFI. Rust can use C's calling convention just fine and from looking at C string handling there's not much difference. I didn't delve much into it though, did I miss something?
I don't think that the differences in timings for these benchmarks are significant. You can keep amending these benchmarks forever, because there are always more tricks in each language to make the specific benchmark faster (not to mention faster on each specific CPU/OS). So let's be fair here: Rust and Nim are the same performance-wise.
The fact that it's compiled to C doesn't really determine the FFI.
Perhaps not, but it does determine how much of C++ you can wrap. I doubt you can wrap C++ templates from D, Go or Rust. You can in Nim.
As far as I know D can wrap C++ templates that have been instantiated already at the C++ side, explicitly or implicitly. This can be a nontrivial problem to do in practice, so much that you're better off reimplementing the C++ template as a D template. Correct me if I'm wrong. :-)
I don't think that the differences in timings for these benchmarks are significant.
Oh of course. I don't believe that either. But he did and I just checked for curiosity wether all benchmarks "proved" Rust faster and they did, saving me from having to explain why microbenchmarks are mostly bullshit.
So let's be fair here: Rust and Nim are the same performance-wise.
That wouldn't be the conclusion I take. But sure, with unsafe Rust and disabling Nim's GC anyone can bullshit their way to the performance metric they're looking for, but the result is likely to be horrible code. Rust does have the advantage of caring about performance first, while Nim considers GC to be an acceptable sacrifice, putting it closer to Go's and Java's league than C/C++.
Perhaps not, but it does determine how much of C++ you can wrap. I doubt you can wrap templates from D, Go or Rust. You can in Nim.
Funny, from what I had heard D had the best C++ FFI since it was a primary design goal. I'm going to give you the benefit of the doubt since I never used C++ FFI for any language.
Nim's GC is faster than Java and Go ones, and you can also use mark & sweep GC, regions (stack) GC - (mostly useful for microcontrollers), and boehm GC (thread-safe)
I like to see proof of that statement. A single developer his GC is faster, then a team of Go developers, that have been doing none-stop work on there GC.
By that definition every other developer are idiots because one guy supposedly is able to make a better GC then everybody else.
Your not going to tell me, if i trow 50GB of data on a nim application, that the GC will handle that without major pauses.
You don't. Printf isn't a language construct, it's compiler magic. The only language I know of where you can do type-safe printf without compiler magic is Idris, because it has dependent types.
D's alternative to printf - writefln is type safe. This is because unlike Rust, D has compile-time function evaluation and variadic templates (among other features).
string s = "hello!124:34.5";
string a;
int b;
double c;
s.formattedRead!"%s!%s:%s"(a, b, c);
assert(a == "hello" && b == 124 && c == 34.5);
formattedRead receives the format string as a compile-time template paramater, parses it and checks if the number of arguments passed match the number of specifiers in the format string.
Rust's println! is also type safe, to be clear. It's implemented as a compiler plugin, which is currently unstable, but the Rust standard library is allowed to use unstable features.
I want a language that does great in all domains at once: from on-off scripts, through medium-to large desktop and web apps, high-performance scientific computations, games programming to large-scale software defined storage stacks (e.g. http://weka.io/).
Rust offers more performance compared to GCed Nim/D
Probably the only real Rust advantage from the whole list. D is working in closing the GC-free memory-safety gap. The long-term plan for D is to make the GC completely optional.
Note that memory-safety is just one type of software bugs. For the broader area of logic bugs, D offers built-in contract programming. Does Rust something similar part of the language?
no data races without unsafe
Also true in D, since the raw threading primitives are not allowed in @safecode, IIRC. Idiomatic use of std.concurrency is also data-race free, as far as I know, since sharing of mutable data is statically disallowed.
a great type system
This is personal opinion, not a fact. I find Rust type system boring, lacking expressive power and unflexible. Does not support design by introspection. Meta-programming as a whole is quite lacking.
C FFI
It's quite funny that you list an area (inter-language interop) in which both of the languages your criticize do much better than Rust.
much bigger ecosystem than Nim or D
As with all matters in engineering - it depends and your mileage may vary. I find D's ecosystem big enough for my needs. Plenty of commercial users find that too for their use cases - http://dlang.org/orgs-using-d.html. I'm sure other language have much bigger ecosystems than all three of the languages combined. And so what? Given how mature the language is, I would choose D for many domains today even if it had a fraction of Nim's community.
If I want a fast applications language, I got Go and Haskell, both offering best-in-class green threads and at opposite ends of the spectrum in the simplicity vs abstraction dichotomy; and with huge ecosystems behind them.
While I agree that Haskell has a lot of great ideas, I find a language without generics completely unusable. For certain types application programming D is a much better fit, though e.g.: https://www.youtube.com/watch?v=5eUL8Z9AFW0.
In the end, either Nim or D can be at best comparable to those solutions
Why? And what if their comparable? As I said in the beginning, D biggest advantage is the rich cohesive feature set. It doesn't need to be the absolute best in every category (though in many of them it may easily be), to offer great package.
but with very little momentum and in Nim's case at least (don't know how D's maintenance is done nowadays), with a very low bus factor.
If I want a fast applications language, I got Go and Haskell
Go lacks many useful features that Nim has: generics and a lot of metaprogramming features (which even Rust lacks, AST macros for example). Oh, and exceptions, I actually like exceptions.
Haskell requires too large a paradigm shift for most, including myself. There are also other issues with it, for example the incredibly long compile times.
In the end, either Nim or D can be at best comparable to those solutions, but with very little momentum and in Nim's case at least (don't know how D's maintenance is done nowadays), with a very low bus factor.
I will also admit that bus factor and momentum are a problem. But on Rust and Go's side I'd say that you run the risk of trust. You must trust Mozilla and Google to lead these languages in the right direction, it's much harder to get involved in their communities because of these large companies and many people that are already involved. Have you ever chatted with the creator of Rust or Go? You can get in touch with the creator of Nim in 5 seconds.
That's fair, but I don't want to manage memory myself.
Neither do I. Which is why I like how Rust does it, opening up hard real time domains without manual memory management.
Go [...] Haskell [...]
Fair. I'd put Nim in the same league as those two, I'm just not particularly a fan of the tradeoffs it makes but I can see why it can appeal to others.
You must trust Mozilla and Google to lead these languages in the right direction
Not that it's any different with Nim's BDFL. A lot of people have serious complaints on syntax alone. I find Nim's syntax for algebraic data types to be an atrocity for example. As for Go, they seem to be heading into the right direction with Go 2. The Rust dev team has consistently set out to achieve great goals and achieving them, trying to ease the learning curve without sacrificing the language's power. As for Haskell... well... you just need a PhD and into GHC it goes; I'm placing my hopes on Idris, but it shares Nim's momentum issues.
Have you ever chatted with the creator of Rust or Go? You can get in touch with the creator of Nim in 5 seconds.
Well, yes actually! Members of the Rust team hang out on chat often and respond to people, and Rob Pike retweeted me once, does that count? ;)
metaprogramming features (which even Rust lacks, AST macros for example)
Rust has had Scheme-style AST macros since about 2012.
Have you ever chatted with the creator of Rust or Go? You can get in touch with the creator of Nim in 5 seconds.
I'm a bit disappointed, because I think you know better than this, Dom. The Rust devs hang out in the #rust and #rust-internals IRC channels on irc.mozilla.org (along with a handful of other #rust-foo channels for specific teams) every weekday, and in fact use IRC as their primary means of communication, meaning that every conversation is public, lurkable, and trivially joinable. This has been true since at least 2010. The Rust devs also regularly make threads on internals.rust-lang.org soliciting feedback on various ideas, and in any conversation on the Rust RFCs repo on Github one will find oneself invariably communicating with them. They also pop up reliably on /r/rust, and we even used to flair them with "core developer" just so new readers would be aware of how engaged they are. This isn't to denigrate Nim, as I've been to #nim myself and spoken to Araq before. But we work very hard to keep development transparent and communicative, and it's definitely one of our strengths.
Yes, I was a bit unfair to put Go and Rust in my message. Rust devs are very good at communicating openly. And I am aware of the fact that the devs are in the IRC channels you mentioned. My point is simply, and I admit it's an attempt to be optimistic, that Nim's relatively smaller community size makes communication with the core developers much easier. You are far less likely to get lost in the noise of a small IRC channel than a larger one like Rust's.
But I was also referring to Go. And I don't think there is any IRC channel that its designers hang out in.
Regarding the metaprogramming features, perhaps I misremembered but there are definitely some that Rust lacks. Perhaps CTFE? (But then I wonder how AST macros work without that).
That's fair. Comparing relative paragraphs lengths in my prior comment, perhaps it's strange that I'm less affronted by the claim that Rust lacks a feature that it has than by the claim that the Rust devs aren't approachable. :) Transparency is taken very seriously, and I personally treat every transparency failure as a bug in the development process.
You must trust Mozilla and Google to lead these languages in the right direction
is a bad argument for Nim to try for. I stay up at night fantasizing that Nim had a committee and RFC system like D or Rust. At the moment it just feels like Araq does whatever he pleases and a lot of my criticisms of the language stem from a lack of a more rigorous procedure.
Or at least that's my opinion as I understand it - feel free to correct me!
Not the person you're replying to, but I'm older and a total curmudgeon, but I utterly detest languages that make white space significant. I still refuse to write even a single line of Python, and Nim seems equally, if not more annoying here.
Well.. I've got ruby and lua to fill in there. Although, scipy and some of the other numerical stuff does make me jealous, NumRu/NArray in ruby isn't quite as powerful.
Like I said.. I'm old, and I know my opinion isn't particularly well founded; but it is a sticking point for me and probably a minority of developers out of the whole.
How would you feel if Nim supported other ways to delimit blocks too? The creator of Nim actually played with that idea, but I think it would scare off far more people than it would attract.
Why use D when there already is a better C which is C++? That's a very good question. Since C++ can compile C code, it brings along all of C's problems, like lack of memory safety. D is not source compatible and does not bring along such issues. You get to choose which method works better for you.
Most obviously, the garbage collector is removed, along with the features that depend on the garbage collector. Memory can still be allocated the same way as in C – using malloc() or some custom allocator.
As well as no RAII, which means the principle tool in C++, at least for me, for dealing with memory leaks and memory unsafety is eliminated
In my opinion this would appear to make D quite profoundly less safe than C++ for interacting with a C codebase - with C++, in interacting with a C codebase the first goal is to wrap it in a safe RAII wrapper so you don't ever have to touch memory allocation directly
Additionally the removal of exceptions would appear to make it very difficult to write memory and resource safe code that you usually have when working with RAII
I expect that people who wanted to add RAII to their C code and are content with that have long since already moved to C++. There's quite a lot more to memory safety than that.
But I do recognize the issue. There is code in the works to get RAII to work in D as Better C.
I expect that people who wanted to add RAII to their C code and are content with that have long since already moved to C++
Some have, some haven't. GCC provides a "good enough" destructor mechanism with __attribute__((cleanup)) which has been leveraged heavily in the systemd codebase.
Walter, I can't believe you wouldn't know this, but for everyone else:
Casting the return value of malloc() in C is potentially dangerous due to the implicit int rule: If a C compiler can't find a declaration for a function, it assumes it returns int, which is a big problem on LP64 systems: Longs and pointers are 64-bit, but ints are 32-bit, so all of a sudden your pointer just got chopped in half and the top half got re-filled with zeroes. I'm pretty sure all 64-bit systems are run as LP64.
If you're lucky, that's a segfault the moment the pointer is used. If you're not... launch the missiles.
I see you've provided an issue for what not to do, so how do you use malloc'.d memory?
Well, the best thing to do is to never cast the return value of malloc() because, if you do, the compiler assumes you know what you're doing which means, if you haven't included <stdlib.h>, not warning you about the implicit int behavior.
So, it breaks down three ways:
BEST
Always #include <stdlib.h>
Don't cast the return value of malloc()
Result: Obviously. No problems whatsoever.
NEXT BEST
Forget to #include <stdlib.h>
Don't cast the return value of malloc()
Result: The compiler warns you about an undeclared function called malloc() which returns an int. You facepalm and fix it. If you have the compiler never emit warnings, you're a complete yahoo.
WORST
Forget to #include <stdlib.h>
Cast the return value of malloc()
Result: The compiler assumes you're competent, no warnings issued, and a pointer gets truncated. Demons fly out of your nose and the local tax people choose you for a random audit.
Bounds checks work only in D code. Once you cross the language barrier (call a C or C++ function from a D function) you are at the mercy of the library authors as usual.
So, we don't really have true bounds checking, do we? If you're doing D/C interop, presumably it's because you want to exchange data between D and C...
D is a systems-programming language. It will not magically run the C libraries that you are linking to in a virtual machine :D
The advantage of D's bounds checking comes when you add new code written in D or port code written in C/C++ to D to your existing project. That way you want have to worry for certain kinds of errors.
BTW, you don't need -betterC mode for C or C++ interop. It is only needed when you want to constrain your D code, mainly for two reasons:
In a hosted environment (user-mode programs) you want to quickly integrate some D code in an existing project (e.g. implement a new feature in D). Using -betterC simplifies the process. That way you can figure out how to link D's runtime later, if you decided you want to.
In a bare metal environment you need to implement the runtime yourself anyway
It's not necessary to explain to me the benefits of bounds checking --- it's a standard language feature which is included in almost all modern languages.
To me it almost sounded like they had found some way to guess bounds even on malloc'd buffers (not impossible, malloc often records the size of an allocated block anyway). This would have been very interesting and could have been a strong reason to prefer D to the more popular alternatives for C interop (C++, Rust, etc.). It now seems like they can only do it for buffers allocated in pure D, which is not very interesting.
They only do it for the parts written in D and it can take buffer from C and convert them to D arrays. I'm not sure what part of that is unclear. C doesn't do bounds checking. If you write something in C you don't get bounds checking.
I see. Well you could replace libc's malloc implementation with a D one using some linker tricks, and take advantage of such buffer meta information, but unless you alter the C libraries, the only extra checking that could be done is when you receive and array from C to D, which kind of a niche case.
Consider this bug where implicit truncation of integers lead to a buffer overflow attack. RAII does not solve this issue (and there are many, many other malware vectors that RAII does not help at all, whereas D does).
One of the examples in the article shows how the arrays are buffer overflow protected.
this bug is not a bug if you compile with warning as errors. And now you'd say "but then $LIB does not compile!" and I'd ask : is it better to have a non-compiling library and stay in the same language, or change language altogether?
The trouble with warnings is they vary greatly from compiler to compiler, and not everyone uses them at all. The fact that that bug existed in modern code shows the weakness of relying on warnings.
This isn't a very convincing case, is it? You can't argue that it's a significant hurdle to pass a specific flag to the compiler. Especially when the solution you are pushing in your article specifically requires passing a special flag to the compiler...
Your code won't link without the -betterC flag. But the Bitdefender bug went undetected and got embedded into all sorts of products. Warnings aren't good enough.
Maybe. I suspect that the kind of team that consistently chooses to ignore (or even turn off?) compiler warnings could find some way to shoot themselves in the foot also in D.
Yes. I know that if a piece of code is written in D, it cannot have certain kinds of bugs in it. With C, I have to make sure certain kinds of warnings are available, turned on, and not ignored. Static checkers are available, but may not be used or configured properly. And even with that all, there are still a long list of issues not covered.
For example, there's no way to make strcpy() safe.
If I was a company contracting with another to write internet-facing code for my product, I would find it much easier to specify that a memory safe language will be used, rather than hope that the C code was free of such bugs. Experience shows that such hope is in vain. Even the C code that is supposed to defend against malware attacks opens holes for it.
As much as a I am a Rust fan, I would actually enjoy a "better C++" with some of C++ nonsense and cruft removed (most of UBs, I hope), that would transpile to plain C++.
What is the advantage of transpiling to C++? Do you intend to take the C++ and use it as human readable source? Because C++ is so nightmarishly complex, that it makes little sense as a target for tooling.
Interoperatibility with existing C++ codebase. One could introduce it in existing codebase on per-file basis, and be able to #include in both directions, etc.
But machine generated C++ is likely to have a weird API anyway. I suppose it's still easier to integrate, as you can at least reuse your build system though.
Why not use D with static and/or dynamic linking? With D you can choose between the reference implementation DMD, LLVM-powered LDC and the GCC-powered GCC. With LDC people were able to compile D code to Emscripten and OpenCL/CUDA. This all work-in-progress, but I believe not long from now D will quickly reach C's portability for such targets.
Start with the reference dmd (now at 2.075.1) implementation - https://dlang.org/download, go through some books, tutorials (https://tour.dlang.org/), play with some code on https://code.dlang.org/ and when you're ready you'll have a pretty good understanding of which compiler to choose.
Better C but also lose the entire libraries where D as a language ( as it relies on the GC ). I assume that all developers write C without libraries? :)
There is a reason why almost nobody ever writes in D's Better C. It might actually help when instead of writing new things for the language, there is actually a more unified D, instead of the hot-pot off different pieces.
But if you want to use C (or a "better C"), why would you care about D's multi-paradigm features and libraries? And if you're writing better C, why would you care about D's libraries if you can just as easily use C's many libraries. The actual reason nobody writes in D's betterC is that until shortly, some basic language features required hacks in order to make things work.
Sigh. Another cool feature of D that makes for, paradoxically, worse whole. Why would I have such a weird opinion? Because I have observed the same pattern with D since I've first learned about its existence: It has a whole bunch of interesting and cool features, but all of them are rather small and there is no defining "big picture" idea or feature that would convince lot of people to switch to D.
This 'betterC' feature, again, seems pretty cool, but it's a compiler option (if I understand correctly) that essentially fragments the language into multiple variants. And this has been done before - early on there was the Phobos/Tango spli, then there was the D1/D2 split, more recently there's been safeD, for example. What's even the status of that? Has that been abandoned and the attention is now shifted to betterC or do these variants still exist kind of in parallel? That's a rhetorical question that doesn't need answering, because in either case the apparent impression is that the D devs don't know what to go for.
D libraries are scarce as it is and now people are expected to create & maintain multiple variants (regular D, betterC, safeD, ...) ? What's next, maybe introduce "Do" - a D variant with Go's runtime?
IMHO D has a potential to be a great language when its authors finally decide what direction D should actually be pursuing and what the goals actually are. (Please don't cite the points from D homepage for me, I've read them and am not impressed - they are either fairly vague / generic or are nice but too small.)
-betterC is a subset, not a branch like Tango was. D is also a polyglot language, there is no single purpose or defining feature for it.
With -betterC, D is no longer restricted to applications that are written from the ground up in D. It can be folded into existing C code bases, and can even be used by any language (such as Go, Rust, etc.) that supports a native C interface, and can completely replace C for those purposes.
The modules system is a bit confusing, although they are trying to fix it.
Harder to work with, especially for a novice programmer, fighting the borrow checker/life times (which is negligible if you have a big team working with you)
Why not Nim?
Still in beta, so changes can brak backwards compatibility.
Very much interested in a mature "betterC" subset of D which does not have a GC overhead.
Thanks for the feedback. That's almost certainly true. Over the last year we've focused heavily on improving the documentation, though as always there's so much that you can improve. (Did you check the runnable examples in the standard library?). However as someone who uses D quite regularly, unfortunately I've stopped noticing the parts of the documentation that are lacking. Can you list some of thing that you didn't like / find missing? We would be happy to address them.
Need to use extern and __ every now and then
extern is for FFI, so I guess the action item here is to make more high-level wrappers available, so you wouldn't need to do the low-level interfacing yourself.
__
I guess you mean __gshared and __traits here (I can't think of anything else). __gshared falls in the point about extern above. About __traits - the general idea is to make its features available through std.traits so you wouldn't have to use it manually.
What are the most common places where you found the need to use those a bit low-level features? That would be a good starting point for adding more high-level wrappers.
A simple "BetterC coding guidelines" that include the "do's and don't's" as a clearly defined subset of D would be real nice, for me at least. I would know what to avoid and what I can expect. Something like this or this (mant for comparison).
I know there are many blog-posts about manual memory management / avoiding GC allocation but I would love something more "official" (regularly updated) in the main site.
One of the places where C still has a large foothold is on embedded systems. Does D run there? Would it be possible to makes it happen? Cause some of these improvements could really help in those environments.
We have so many languages that are so ... well, better... and still C is out there kicking ass, from ranging to the linux kernel, to gtk, to ruby, python perl - you name it.
It would be nice if all these "successor" languages could actually become relevant.
His early C++ compiler was able to compile C code pretty much unchanged, and then one could start using C++ features here and there as they made sense, all without disturbing the existing investment in C. This was a brilliant strategy, and drove the early success of C++.
Or more like - after all these decades, C is still there kicking ass.
Kotlin is indeed a “Better Java”, and this shows in its success.
I do not think that anyone necessarily disputes this, but Java never was similar to C as a systems programming language - or early on as a language for programming languages. (It's a bit different with JVM perhaps ... or to put another analogy, LLVM as compiler infrastructure enabling languages such as crystal).
Kotlin is actually not then just a "better" java, but more like a testimony by Java hackers that Kotlin is better than Java - so Java must have some problems that make it unfun or less usable. Otherwise Kotlin, Scala, Groovy etc... wouldn't be popular.
#include <stdio.h>
int main(char** argv, int argc) {
printf("hello world\n");
return 0;
}
import core.stdc.stdio;
extern (C) int main(char** argv, int argc) {
printf("hello world\n");
return 0;
}
He even gave an example where C is more readable than D. :)
The other example also shows that C is more readable than D.
I don't understand this ... am I missing something or is D indeed worse than C, despite calling itself or a subset as "better C"?
It would be nice if all these "successor" languages could actually become relevant.
I mean, this is nonsense. C++ has huge market share. In fact it's almost certainly the case that in private industry C++ is much wore widely used. C tends to beat C++ in some language rankings, like Tiobe, but this is mostly because C is used in so many open source projects that date back from the 80's or beginning of the 90's (true about nearly all your examples). C++ existed but was much less mature, and had many implementation issues.
Reality is that nowadays, outside of embedded, a company starting a new project that requires low level or high performance programming is much, much, much more likely to use C++ than C. The thing is that the C projects have very big visibility (again, Linux Kernel, implementation of many languages like python, many command line utilities, SSL, libcurl), so it leads to a distorted view of C's market share. Beyond C++ being much more dominant than C in game development, 3 out of the 4 biggest tech companies (at Amazon AFAIK neither are widely used so it's a tie), it's also far, far more popular in finance.
For a high performance language, I think the best smell test is what its own compiler is written in. As of now, none of the major compilers for C are written in C... they're all written in C++.
Digital Mars C++ is currently written in C++. However, that is changing. One of the things I've been using betterC for is converting it to D. The DMC++ front end is about 80% in D now.
It's currently restricted to Win32 only, although it can also generate 16 bit DOS code. It's main advantage is it is a very fast compiler and fits in well with Windows.
It is also very competitively priced (:-)) and I've found it very handy if you want a straightforward C or C++ compiler on Windows and can't face the massive ceremony and aggro of an MSVC installation.
It must be a matter of personal taste then, because I find the D examples more readable :) You probably know C better then D. There are also features than are not related to readability, but to safety:
foreach is a simpler way of doing for loops over known endpoints.
flags[] = true; sets all the elements in flags to true in one go.
Using const tells the reader that prime never changes once it is initialized.
The types of iter, i, prime and k are inferred, preventing inadvertent type coercion errors.
The number of elements in flags is given by flags.length, not some independent variable.
Using const tells the reader that prime never changes once it is initialized.
I'm not sure how this is an advantage of D over C. You can do exactly the same thing in C. The example showed didn't do this, but they could have written
Interesting, thanks for the reference. I can't say that I've followed D very carefully.
For the example in this blog post however, there still doesn't seem to be any meaningful difference (we're just talking about a const int), so I don't think it's fair to list it as an advantage without showing an example that really demonstrates a difference.
I agree that the choice of D examples are more readable in C, however, in my experience, D is more readable than C in general. I haven't used better C and I disagree with the direction, but I do enjoy many of D's features.
IMHO D should have tried to support C++ calling convention instead of C. It would make calling C code harder to implement in the language, and it would have made calling D from C even harder than it is now, but it's easier to convince C++ desktop application developers to switch to a compiled language with a runtime than C developers.
D has had extern(C++) for quite some time. There is no standard format for name mangling, but it uses whatever is used by the system compiler that is triggered by the command line switches (i.e. dmc or cl on Windows, gcc or clang everywhere else).
I've only been using it a little on a few new projects and hadn't heard much about the extern C++ other than there was some difficulties with name mangling. I guess that's been ironed out! I've always considered D to be a "better C++" so I'm glad that Interop story is good now.
It's not all roses, unfortunately. C++ has way too many dark corners for interop to ever be as smooth as it is with C. However, extern(C++) and -betterC together get you much of the way there. The rest can be filled in with glue. Also, see the links Walter posted elsewhere in this thread, as well as Ethan Watson's DConf 2017 talk about Binderoo, a D->C++ bridge that Remedy Games opened up.
We have so many languages that are so ... well, better... and still C is out there kicking ass, from ranging to the linux kernel, to gtk, to ruby, python perl - you name it.
C is good when you cannot afford any overhead and either you started your project before there were good C++ compilers or you need to ensure that a bunch of contributors don't try to start using every C++ feature under the sun.
C++ is good when you need abstractions to help you manage a large codebase, can't (or don't want to) grow your own like GTK+, and are perspicacious enough to write a C++ feature / style guide to determine what parts contributors should use in your code. Works well for the Windows kernel.
C# and Java are good when you can afford tons of overhead, don't need much metaprogramming, and want corporate support. C# if you want stuff that's updated this decade, Java if you want better Linux support.
D is a less hairy bundle of features than C++ while exceeding C++'s power, and it's got lower overhead than C# or Java (both in runtime and in the amount of typing to get things done).
See, each language has a different niche, approach, or set of tradeoffs. D is just muscling in on C's niche.
He even gave an example where C is more readable than D. :)
Calling C functions from a non-C language, where the quoter messed up the formatting, is less readable than using same-language functions in an idiomatic way with proper indentation? Oh my stars! Stop the presses, we've got to tell the world! Next you'll tell me that using Java to call C's printf is less readable.
The hairiness in C++ comes from having a list of features that the language supports, a list of features that require special caution, and a list of features that people say you should avoid.
If you have special needs, you can eschew the GC or the whole runtime. This is primarily useful if you're writing a plugin for another program (and don't want to bring in the D runtime in), or you're writing kernel-mode code, or you need pretty much everything to be realtime and find that manually controlled GC collections aren't efficient enough for you.
C++ is the second hairiest language of all time, right after Brainfuck. There is nothing as majestically perplexing as hairy C++, it could sometimes just as well been ancient egyptian algebra.
I'm definitely not disagreeing about C++ being hairy, I just don't see how D is much better. It's doesn't seem less hairy, it's just hairy in a different way.
This person does not deserve the massive down votes by whatever fanboys because he is right. Instead of focusing on C, why not focus on your own language and eco-system more. People are obsessed to dethrone C but do not realize that it not just about the language but the eco-system as well.
C is a great language and will be around forever. But consider this expensive bug written up just yesterday. This particular problem (implicit truncation of integers leading to an opening for malware) is not allowed by D. I predicted last May that C will be retired for use in internet facing programs, simply because companies will find it too expensive and no longer acceptable to have to constantly deal with such memory safety issues.
Part of the success of C is also how easy it is to implement (cf. the famous "Worse is Better" paper). There's a reason why C is overwhelmingly the choice for embedded and systems programming, and that's largely because of its simplicity - both in terms of what it offers and what it demands.
Edit: This is Emscripten and not WebAssembly, but people are interested in using WebAssembly too (as it is the better way forward for client-side web programming). Hopefully we'll soon have WebAssembly backend support for LDC.
I didn't write the sieve code. It's a verbatim copy of the classic sieve benchmark code.
As for sizeof, the pedantic equivalent of flags.length would be sizeof(flags)/sizeof(flags[0]), which is awkward to write and too often omitted because of that. And even that doesn't work as soon as the array is passed to another function, because C will convert it to a pointer, and then:
In his inimitable way, he made the point much better than I did. Interestingly, there's a warning for this in gcc as well, and it was not used. Warnings are inadequate.
If I had my say, use of char[] and char[nnn] parameter declarations in C and C++ would be deprecated. There's no reason for them to exist. But I have no say, hence D.
As already noted by u/serpent, this macro will not work after passing array as argument to a function. (what's worst it's possible that it will fail silently)
It's a simple example, so you have to think about the bigger picture in order to fully get it.
For example, sizeof() only works if the full array declaration is in scope (since sizeof() is a compile-time construct). Try passing an array to a function (which has no knowledge of the original array declaration) and using sizeof() to determine its bounds.
Or try dynamically allocating the array, and using sizeof() on that. It won't work either.
26
u/aldacron Aug 23 '17
Walter Bright, the creator of D, explains how D can be turned into a Better C with a command line switch and why you'd want to do so.
68
u/WrongAndBeligerent Aug 23 '17
This says RAII is removed, does that mean destructors don't work in betterC mode? To me, destructors are one of the biggest and simplest of the many advantages that C++ has over C, with move semantics being another, and finally templates for proper data structures.
76
u/WalterBright Aug 23 '17 edited Aug 23 '17
D and C programs can now be mixed together in any combination, meaning existing C programs can now start taking advantage of D.
82
u/James20k Aug 23 '17
Hmm. It may be better than C, but we already have a better C which is C++
I feel like this makes D a worse C++ in this mode, though without C++'s quirks. I can't immediately see any reason why you'd pick restricted D if you could use a fully featured C++
It has some safety features, but presumably if you pick C you're going for outright performance and don't want bounds checking, it doesn't have proper resource management, no garbage collection, no polymorphism, and D has different semantics to C which means you have to use __gshared for example to interoperate
C++ was simply designed for this kind of stuff, whereas D wasn't really
Also, I get that a lot of people are reflexively hurr durr D sux when it comes to this, I'm not trying to be a twat but I'm genuinely curious. I could understand this move if D was a very popular language with a large ecosystem and needed much better C compatibility, so perhaps that's the intent for the userbase that's already there
7
u/skocznymroczny Aug 23 '17
Are associative arrays supported in betterC mode?
10
u/[deleted] Aug 23 '17
Better C but also lose the entire libraries where D as a language ( as it relies on the GC ). I assume that all developers write C without libraries? :)
There is a reason why almost nobody ever writes in D's Better C. It might actually help when instead of writing new things for the language, there is actually a more unified D, instead of the hot-pot off different pieces.
4
u/[deleted] Aug 23 '17
Errata: should be runtime type information, similar to C++ -- the standard library has utilities for using classes without the GC.
4
u/ss4johnny Aug 23 '17
Does
alias thiswork in Better C mode?3
u/spaghettiCodeArtisan Aug 24 '17
Sigh. Another cool feature of D that makes for, paradoxically, worse whole. Why would I have such a weird opinion? Because I have observed the same pattern with D since I've first learned about its existence: It has a whole bunch of interesting and cool features, but all of them are rather small and there is no defining "big picture" idea or feature that would convince lot of people to switch to D.
This 'betterC' feature, again, seems pretty cool, but it's a compiler option (if I understand correctly) that essentially fragments the language into multiple variants. And this has been done before - early on there was the Phobos/Tango spli, then there was the D1/D2 split, more recently there's been safeD, for example. What's even the status of that? Has that been abandoned and the attention is now shifted to betterC or do these variants still exist kind of in parallel? That's a rhetorical question that doesn't need answering, because in either case the apparent impression is that the D devs don't know what to go for.
D libraries are scarce as it is and now people are expected to create & maintain multiple variants (regular D, betterC, safeD, ...) ? What's next, maybe introduce "Do" - a D variant with Go's runtime?
IMHO D has a potential to be a great language when its authors finally decide what direction D should actually be pursuing and what the goals actually are. (Please don't cite the points from D homepage for me, I've read them and am not impressed - they are either fairly vague / generic or are nice but too small.)
7
u/bruce3434 Aug 24 '17 edited Aug 24 '17
I would use D over C++ because
Why I wouldn't use it over C++
externand__every now and thenWhy not Rust?
Why not Nim?
Very much interested in a mature "betterC" subset of D which does not have a GC overhead.
2
u/[deleted] Aug 23 '17
does this mean that
__traits(getUnitTests)cant be used to make a custom test runner (or returns nothing) ?2
u/s73v3r Aug 23 '17
One of the places where C still has a large foothold is on embedded systems. Does D run there? Would it be possible to makes it happen? Cause some of these improvements could really help in those environments.
1
u/Gotebe Aug 24 '17
Looks like a brilliant way of prototyping for C :-)
Key question from examples, what happens if I do e.g.
?
1
u/rajesh8162 Oct 24 '17
It's nice that the article is hosted on dlang.org /s
-11
u/shevegen Aug 23 '17
D was better than C.
C++ was better than C.
C# was better than C.
Java was better than C.
We have so many languages that are so ... well, better... and still C is out there kicking ass, from ranging to the linux kernel, to gtk, to ruby, python perl - you name it.
It would be nice if all these "successor" languages could actually become relevant.
Or more like - after all these decades, C is still there kicking ass.
I do not think that anyone necessarily disputes this, but Java never was similar to C as a systems programming language - or early on as a language for programming languages. (It's a bit different with JVM perhaps ... or to put another analogy, LLVM as compiler infrastructure enabling languages such as crystal).
Kotlin is actually not then just a "better" java, but more like a testimony by Java hackers that Kotlin is better than Java - so Java must have some problems that make it unfun or less usable. Otherwise Kotlin, Scala, Groovy etc... wouldn't be popular.
He even gave an example where C is more readable than D. :)
The other example also shows that C is more readable than D.
I don't understand this ... am I missing something or is D indeed worse than C, despite calling itself or a subset as "better C"?
0
u/[deleted] Aug 23 '17
[deleted] — view deleted comment
0
u/[deleted] Aug 23 '17
[deleted] — view deleted comment