This is from a paper that I wrote for one of my classes recently and would like to share it here.
Nearly every native application running on modern computers was written using C++. The language has reigned king of natively compiled programming languages for nearly 3 decades now, but the field of computer science is one of rapid change and 30 years is a very long time especially in terms of computers (History of C, n.d.). Is a 30 year old tool still up to the task of writing the next generation of software? A lot of developments have been made in the field of compiler technology over the last few decades so maybe it is time to consider more modern tools. In the next few paragraphs I will introduce one of the new kids on the block, the D programming language, and discuss the many features that it improves on from C++. Since D is a natively compiled language, we will almost exclusively compare it to C++ because it is the most widely used of natively compiled languages. D is considered by many to be a better C++, but as we will see, D is much more than that. With the right amount of work and guidance from its users, D has the potential to surpass C++, and become the next king of natively compiled programming languages.
What is the D programming language? D is a statically typed, natively compiled, multi-paradigm language. The language has a syntax that is strongly influenced by C and C++, as well as other languages such as Java and C#. It was created by a well known expert in compiler technology, Walter Bright, in 2001 as D1 and reached its first stable release in 2007 as D2 (Bright n.d.). When D2 was introduced, backwards compatibility with D1 was dropped in order to improve the language. Since D2 is the only supported implementation, D2 is just simply called D. The language has come a long way since it was first introduced. There are now 3 major implementations: DMD which is the official compiler, GDC is the gnu compiler backend with a D front end, and LDC is the LLVM compiler backend with a D front end. All implementations of D come packaged with its standard library called Phobos. The D language was originally created to be a better implementation of C++, but has grown to be much more, borrowing ideas from other languages such as python, ruby, and java. It is an elegant language, which has a syntax nearly identical to C++, but takes many of the things that frustrate C++ programmers and offers a cleaner alternative. These consist of but aren’t limited to: Garbage collection, macros, modules, and multi-paradigm programming. Of course there are many other unique features of D, but these are the ones that we will be focusing on here.
D, like most languages today, has support for multiple programming paradigms: Object-oriented, functional, imperative, concurrent, and contract. C++ actually includes support for all of these paradigms, but what makes D superior is the way it implements many of them. Unlike C++, D was designed with these programming paradigms in mind. This leads to a much more elegant and natural feel when using these features. A great example of this is design-by-contract, or contract programming. In C++ it looks very clunky and out of place, however in D, contract programming fits perfectly with the rest of the syntax. Contracts are a very new way of programming but it doesn’t take long to realize how powerful they can be. Contracts ensure that everything is functioning as it is intended to. This helps prevent errors that the compiler normally wouldn’t catch. D also has strong support for functional programming through pure functions and closures. The functional aspects of the language feel natural because of the syntax. Pure functions are simply declared with the keyword “pure” following the parameter list, and closures use the “=>” operator to declare the body of the function. Functional programming in C++ isn’t quite as simple. Closures in C++ are done using lambda expressions which require multiple sets of parenthesis and braces. When it comes to the object-oriented parts of D, there is little difference from C++ except for the way constructors and destructors are handled. D supports C-style structs which are value types, as well as classes which are reference types. One major difference to note is that D doesn’t support multiple-inheritance like C++ does. Instead D offers single inheritance and unlimited interfaces. This is controversial, but according to Andrei Alexandrescu, single inheritance makes code less complicated and with interfaces you can achieve almost anything that multiple-inheritance allows for (Alexandrescu, 2010).
One of the oldest and probably most confusing parts of C++ is the C preprocessor. The C preprocessor is a powerful tool that helps to make the language more flexible by adding conditional compilation, multiple-include guards, and other features. While it is very useful, it has a few flaws. The C preprocessor was written as a separate part of the compilation processes. As the name suggests, it processes the source files before the compiler does, and it changes the source based on the certain language directives and macros used by the programmer. This leads to two main difficulties. The first is that there is no way for the compiler to know what the preprocessor knows, and can lead to problems with debugging as a result. The second is that the preprocessor has it’s own language, so a programmer learning to program in C++ essentially needs to learn two languages (Ernst, Badros, & Notkin, 2002). This is another area where D shines. D offers all of the features that the C preprocessor offers, but these features are built into the core of the compiler. This allows D programs to have a much cleaner syntax, and makes it easier to write and debug code. D uses the “version” keyword for conditional compilation, and as we will see next, include guards are implemented through D’s module system.
Figure 1.1 shows that the C preprocessor way of doing conditional compilation doesn’t require any more code than D, however the D way fits with the rest of the language’s syntax. Another important addition to D that helps to replace the preprocessor is it’s string mixins and template mixins. A mixin will insert a string into the current context as if it were actually code, and a template mixin is a set of declarations that when inserted will act in the current context. This is nearly identical to how the C preprocessor works except since mixins are part of the language, the compiler is able to know more from them for debugging purposes.
Any modern programming language needs to have some way to support code reusability, and it needs to do it well. D borrows from Java and Python in this area by using what are called modules. A module is a source file that can contain class definitions, functions, etc. Multiple modules can be grouped together inside a folder to create a package. A source file can include any number of modules by using the “import” directive. The D compiler automatically checks when importing files and will not import the same source file more than once. This is in contrast to C++ which requires C preprocessor code around each header file to prevent multiple includes.
As you can see in figure 1.2, the C++ code requires 3 lines each beginning with the “#” symbol, and the D code only requires one line which is “module foo;”. C++ also uses separate header files and implementation files for declaring and implementing classes and functions. While this comes with a few benefits, and can make the code more easy to read, it is often cumbersome to do and complicates things. In D, classes and functions are declared and implemented at the same time. The module system in D is very versatile and makes it easy to create reusable blocks of codes. Modules remove the need for namespaces since each module has it’s own namespace. As stated in the language documentation, “By default, imports are private. This means that if module A imports module B, and module B imports module C, then names from C are visible only from B and not from A” (D Language Foundation). This helps prevent names from clashing. Importing modules is very flexible in D. It is possible to import a module inside a class definition so that only that class has access to the imported module. Modules can also be imported publicly which means that any file that imports a module will also have access to its publicly imported modules. Another way that D supports code reusability is through its Universal Function Call Syntax or UFCS (D Language Foundation). With UFCS a function that has a type A defined as its only parameter, can be called as a member of a variable of that type. For example if a function is defined as “foo(A var)” then a variable of type A can call it as “A.foo()”. This syntax helps to improve code readability and reusability.
Garbage collection is almost certainly the most controversial feature of D. As Andrei Alexandrescu pointed out in “The D Programming Language,” “There’s a perception that garbage collection is for lazy, junior programmers”(Alexandrescu 2010). Many programmers feel this way, but it may not actually be the case. In languages such as C and C++ a significant chunk of development time is spent on memory allocation and cleanup, and often times it can be inefficient compared to garbage collected languages which cleanup memory when it is most convenient (Spertus). By implementing garbage collection in D, a lot of the errors associated with memory allocation can be eliminated (Alexandrescu 18). Of course there are third party libraries available for C++ so why not just use them instead? As Mike Spertus said in “C++ and Garbage Collection”, there are many difficulties with implementing garbage collection such as DLLs and dealing with pointers since the functionality isn’t built into the language (Spertus, 1997). D, on the other hand, is designed with garbage collection in mind. As mentioned previously, many people feel that garbage collection is slow or for “lazy programmers” so it has lead to much controversy within the D community. One thing that makes D so versatile is that even though the Garbage Collector (GC) is built into it’s core, it also provides support to bypass the Garbage Collector altogether and handle memory allocation manually. A programmer can simply override the garbage collector with the tag “@nogc”. This gives programmers the best of both worlds. Some programmers prefer to explicitly control memory allocation and deallocation the same way they are used to in C++, while others who come from garbage collected languages such as C# and Java can let the runtime handle memory.
Is D the be all end all of programming languages? Certainly not. No language is or probably ever will be. As stated in “D Programming Language” (2010), D is not for small programs as it would probably be better to use a scripted language, and it is probably not suitable as a first programming language since many of the features are advanced (Klinksy p. 14). Unfortunately since D is a relatively new language, it doesn’t have as large of an arsenal of third party libraries and tools as languages such as C++, so it may be necessary to write your own libraries. With that aside, D is excellent at what it does and would be a great addition to anyone’s toolbox. The language offers many modern cutting-edge features such as unittests, contracts, dynamic arrays, basic string types, mixins, scope guards, and much more. D has a very passionate and growing community of developers that are actively improving the language, the standard library, and several third party libraries. If you are looking for a powerful language that can support many programming styles, compiles natively, and is blazing fast, then D may be worth checking out.
Ernst, M. D., Badros, G. J., & Notkin, D. (2002). An empirical analysis of C preprocessor use. IEEE Transactions on Software Engineering, 28(12), 1146-1170. doi:http://library.semo.edu:2275/10.1109/TSE.2002.1158288
Spertus, M. (1997). C++ and garbage collection. Dr.Dobb’s Journal, 22(12), 36-41. Retrieved from https://library.semo.edu:2443/login?url=http://library.semo.edu:2258/docview/202711116?accountid=38003
Walter Bright. (n.d.). Retrieved April 25, 2017, from http://www.walterbright.com/
D Language Foundation. Modules. (n.d.). Retrieved April 28, 2017, from http://dlang.org/spec/module.html
D Language Foundation. Uniform Function Call Syntax (UFCS). (n.d.). Retrieved April 28, 2017, from https://tour.dlang.org/tour/en/gems/uniform-function-call-syntax-ufcs
Alexandrescu, A. (2010). D Programming Language.