First experiences in D

This post is written by our new senior developer, Gavin Norman.

When I started working at sociomantic labs, I was intrigued by the language of choice here for systems code — a new C derivative called D. I’d never heard of D before, but after reading a book on the language I was keen to try it out.

In reading about it, D struck me as an extremely well designed language. Coming from the perspective of a C++ programmer (which has been my primary coding language up to now) it seemed as though Walter Bright (the originator of D) had taken a long, hard look at how C family languages are actually used in practice, and taken that as the driving principle in deciding core features and syntax of the language. Without the requirement to remain syntactically backwards compatible with the (almost 40-year-old) C standard, D is a complete redesign of the language from first principles, and as a result is refreshingly clean, making it a pleasure to use.

In this post I’ll give a quick tour of some of my favourite features of the language so far, and warnings about a few pitfalls which C/C++ developers might encounter when coming over to D.

Packages & modules

On beginning to program in D, the first thing that struck me, as it’s literally the first line of code in every source file, is D’s built in system of code modules, which replaces the old-school mess of header files and #includes used in C and C++. Every D source file (called modules) begins with a declaration of its location in the tree of source folders (called packages) and its own name, like this:

module src.core.Example;

The structures defined in one module can then be simply imported into another as follows:

import src.core.Example;

By the way, there is actually no concept of header files in D – you define what a function/class does when you declare it. This leads to much cleaner, more readable code.

Another really nice feature of D is the way that the public/private protection attributes, which are only used for class members in C++, have been extended to apply to all declarations, including module imports. So if you import a module as private, this means that it won’t be recursively imported into other modules which in turn import your module, preventing namespace pollution.

in/out/ref

Function parameters can be explicitly denoted as input values (in), output values (out) or reference values (ref). Parameters default to in, as is intuitive. Declaring a parameter as out or ref is very useful, and isn’t as rare as the D language reference would have you believe. out parameters are guaranteed to be reset to their default value when the function is called, thus making it actually impossible to pass values into a function through them. ref parameters are slightly more obscure, but are used in the following cases:

  • When passing a struct to a function (structs are pass by value, which often isn’t what you want if you only want to pass a reference to a large struct, or if you need to pass one into a function and have any modifications be visible on the outside).
  • When the function needs to change the properties of an array. Arrays are pass by value by default, but, somewhat non-obviously, this doesn’t mean that the whole array is copied and passed into the function – the “value” of an array is the object containing the pointer to the array, its length and other properties. So a function can modify the contents of an array passed as in parameter, but to modify its properties (like its length, for example) the array object is needed, meaning that the array must be passed as a ref parameter.

Using out and ref you can strictly define the semantics of your functions’ parameters, avoiding the potential confusion which exists in C, where such information would be conveyed through careful parameter naming or commenting, at best.

delete vs. scope

While there is a delete operator in D, I’ve not yet seen it used in any of the code I’ve come across. Without specifically calling delete, all objects which you create are automatically garbage collected (albeit in a very conservative manner). This may at first sound rather worrying – is D promoting a lazy memory deallocation paradigm? However the whole issue is moved into a different ballpark with the introduction of the scope attribute.

scope tells the compiler that the object should be deleted when the current scope is exited. Using this simple attribute it is possible to precisely control when objects are deallocated, but in a subtler and more strongly enforced manner.

Type inference

D is big on type inference. The basic idea is that a lot of the time when you reference an object the compiler already knows what type it is, so there’s not really any need to explicitly state it. A very simple example is the use of the auto attribute:

MyClass my_object = new MyClass;

Can be written in D as:

auto my_object = new MyClass;

A useful and sensible time saver.

Type inference is used in more advanced ways throughout the language, and becomes especially important in the template syntax.

Built in foreach

I need say little more about the usefulness of this simple looping construct. It is worth mentioning though that it is easily overloadable in classes, using the opApply method. Overloaded opApply methods can even provide a completely customised set of parameters to the foreach loop. Got a database class you need to iterate over by index, key and record content? Simply implement an opApply method, which can feed a foreach loop of this format:

foreach ( index, key, content; database )

Also note that D automatically infers the types of foreach iterators (from the matching opApply method), so there’s no need to declare them.

Using opApply like this can result in iteratable classes with a high degree of usability and transparency.

Friendly arrays

As mentioned earlier, arrays in D are more than just a pointer to a memory address – they also keep track of the length of the array (bounds checking!), and expose a number of very useful properties such as reverse, sort, and dup. The latter creates a new array which is a duplicate of the original – very handy!

Another great feature of D is the syntax for array slicing. Say you want to process just the first three elements of an array, the slicing operator allows you to do things like:

foreach ( element; array[0..2] )
{
    // Do something
}

D’s array implementation and syntax helps to reduce the amount of code needed to achieve the sort of simple operations that every program needs to perform.

Templates

While I’ve yet to write a class which makes use of D’s template capabilities, I’ve been very impressed by the way the language’s designers have cleaned up the template syntax. Using type inference, often calling template functions is completely transparent, having the same syntax as calling a normal function. It appears that pretty advanced template functionality is a norm of the language, rather than (in the case of C++) an arcane add-on which causes headaches even for the few experienced programmers who dare to try and use it!

C binary compatibility

As a final note I thought it’s worth mentioning that D code is binary compatible with C, making it trivial to link with any of the vast array of C libraries that exist. All that needs be done is to create a D “wrapper” or “binding” for the library – declaring its functions in D syntax so that the compiler knows how to handle them. This means that D isn’t just some newfangled language in isolation from the rest of the world – D programmers can draw on the enormous resources of the C world, without having to write more code in that ancient language!