D Programming Language: A Hidden Gem

D Programming Language: A Hidden Gem

D is an interesting language. For some reason, I have an emotional connection with this language and I believe that it is the subject of the biggest unfairness story in the history of software development. To me, it is the best programming language in the market for the reasons I am going to state below. However, this language is underestimated, underrated, and despised in the programming circles because it doesn’t have a strong backer. In this article I want to talk about, and give some credit to D, an underrated sibling of C++, Java and C#.

The D programming language (or sometimes referred as Dlang) is designed as a higher-level alternative to C++. Initially developed by Walter Bright in 2001. His purpose was to improve C++ by refining syntax and he brought some features from other languages like Java and Python. Over the years, D has evolved. It is a practical and efficient language for real-world applications.

This is a simple Hello World app in D:

import std.stdio;

void main() {
    writeln("Hello, World!");
}

The D programming language is great for different kinds of software projects. It’s really good for programs that need to talk directly to computer hardware, like operating systems or device drivers. This is because D lets programmers manage memory and access hardware directly. It also has tools that make writing command-line programs easier, helping to quickly create programs that can handle files, text, and user inputs. Furthermore, D is also suitable for making websites and server applications. It can deal with lots of users at the same time and can help make web servers that are fast and reliable. D makes it easier to keep web projects organized and can handle complex tasks well. Even though it’s not the most common choice for web development yet, D has everything needed to build both the front and back end of web applications efficiently. It’s a good option for programmers who want their projects to run fast and smoothly.

Here’s an example of a simple function in D that takes two integers as parameters and returns their sum:

int add(int a, int b) {
    return a + b;
}

void main() {
    int result = add(5, 7);
    writeln("The sum is: ", result);
}

Another important aspect of D is its syntax, which is clean and expressive, especially you are familiar with C-style languages. The language eliminates many of the historical complexities found in C++. Features like contract programming, true modules, and unit testing are built into the language. It shows D’s commitment to modern programming paradigms. These integrated tools and techniques do not only make the language more powerful, but also encourage good programming practices.

This example shows how to define a simple class in D, demonstrating object-oriented programming capabilities:

class Calculator {
    int add(int a, int b) {
        return a + b;
    }
}

void main() {
    Calculator calc = new Calculator();

    int result = calc.add(10, 15);
    writeln("The sum is: ", result);
}

As you can see, not too different from C++, C# and Java.

One of the foundational features of D is its system-level capabilities like C and C++. It provides low-level memory access and manual memory management facilities. This makes D suitable for applications where direct hardware access and control over memory allocation are needed. On the other hand, D also introduces garbage collection, which is not a standard feature in traditional system languages like C++. In D, pointers can be used for direct memory access, similar to C and C++. See example below:

void main() {
    int a = 10;
    int* ptr = &a;
    *ptr = 20;
    writeln("The value of a is: ", a);
}

In D, organizing code is easier and definitely better than in C++. Instead of using separate header and source files, D uses modules like Java does. Just as a reminder, a module is like a box that holds related pieces of code together under one name. This helps prevent mix-ups when different parts of the code have the same names. So, with D, your code is more tidy, easier to handle, and works more efficiently. See the documentation here for the details of modules in D.

One of the mind blowing capabilities of D is built in unit tests. I love it. As far as I know, this is unique to D, no other languages have it. We always talk about Test Driven Development but never talk about the overhead it creates. In D, unit tests can be written alongside the code, and they can be compiled and run automatically. The integration of unit testing into the language simplifies the testing process, making it a natural part of the development workflow rather than a separate task.

See the following example:

int sum(int a, int b) {
    return a + b;
}

unittest {
    // Test case 1: positive numbers
    assert(sum(5, 7) == 12, "Sum of 5 and 7 should be 12");

    // Test case 2: negative numbers
    assert(sum(-3, -4) == -7, "Sum of -3 and -4 should be -7");

    // Test case 3: zero and positive number
    assert(sum(0, 5) == 5, "Sum of 0 and 5 should be 5");
}

void main() {
    writeln("Sum of 3 and 4 is: ", sum(3, 4));
}

Contract Programming, also known as Design by Contract (DbC), allows developers to specify conditions for a function, such as preconditions, postconditions, and invariants. These contracts act as formal, executable specifications of the function’s behaviour. For example, a precondition might specify that a function’s argument must be non-negative, a postcondition could assert that the function returns a positive value, and an invariant might maintain that the internal state of an object remains consistent after the function is executed. By integrating these contracts directly into the language, D enables developers to write more reliable and self-documenting code, making it easier to identify logical errors and enforce correct usage. Here’s a simple example:

class BankAccount {
    private double balance;

    public this(double initialBalance) {
        balance = initialBalance;
    }

/// Withdraws money from the account
/// Preconditions: The amount to withdraw must be greater than 0 and less than or equal to the balance.
/// Postcondition: The balance after withdrawal is less than the initial balance and is non-negative.
/// Invariant: The balance never becomes negative.

    double withdraw(double amount) in {
        assert(amount > 0, "Withdrawal amount must be positive.");
        assert(amount <= balance, "Insufficient funds for withdrawal.");
    } out (result) {
        assert(balance >= 0, "Account balance cannot be negative after withdrawal.");
        assert(result <= balance, "Post-withdrawal balance is incorrect.");
    } body {
        balance -= amount;
        return balance;
    }

    /// Returns the current balance
    double getBalance() {
        return balance;
    }
}

void main() {
    BankAccount account = new BankAccount(1000.00);

    writeln("Initial balance: $", account.getBalance());
    account.withdraw(200.00);
    writeln("Balance after withdrawal: $", account.getBalance());

    // Uncommenting the following line will trigger a precondition failure due to insufficient funds
    // account.withdraw(900.00);
}

In this example, we define a BankAccount class with a method withdraw that demonstrates the use of Design by Contract principles. The preconditions check that the withdrawal amount is positive and does not exceed the current balance. The postconditions ensure that the balance remains non-negative after the withdrawal and that the withdrawal operation correctly reduces the balance. The invariants, though not explicitly defined in a separate block here, are inherently enforced by the preconditions and postconditions to maintain a consistent and valid state of the account balance.

As you have seen above, D is well ahead of its time and all of the popular mainstream programming languages. It definitely deserves a way better place and popularity in the market but somehow it couldn’t reveal its potential. This is why I wanted to dedicate this blog post to introduce D for people who hasn’t heard yet.

Further resources:

Suleyman Cabir Ataman, PhD

Sharing on social media:

1 comment so far

Advanced C# Tips: Don’t Use unsafe for Minor Gains | Coding and Beyond Posted on9:05 PM - 13/06/2024

[…] you really believe that you need unsafe, you may consider using a different language (like C, C++, D, Rust etc.) which naturally supports memory level […]

Leave a Reply