The D Language
The D Language
2015-02-27
Contents
Introduction 1
History 1
Syntax 1
Type Qualifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Variable Declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Pure Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Modules and libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Features 6
Type Inference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Function Overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Delegates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Lazy Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Contract Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Inline Assembler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Memory Management . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Design Choices in D 9
Writability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Readability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Reliability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
First Program in D 11
Which compiler should i use? . . . . . . . . . . . . . . . . . . . . . . . 12
Running D code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Introduction
This report covers the D language history, syntax, features and quick start
guide on how to get started developing with D.The report also compares D with
some common programming languages and discusses differences and similarities
between these.
History
Walter Bright started to work on a new language in 1999. The first release of
D was made in December 2001 and later reached version 1.0 in January 2007.
The first version of D was similar to C++ and concentrated on the imperative,
object oriented and meta programming paradigms. D official run time and
standard library is Phobos, but because of dissatisfaction members of the D
community created an alternative runtime and standard library called Tango.
The problem was that Tango and Phobos were incompatible and you couldn’t
use both libraries in the same project because they ran on different runtime
APIs.
In june 2007 the first version of D2 was released. With the development of D2
meant that the first version of the language had been placed in maintenance and
only received corrections and implementation bug fixes. D2 were going to make
breaking changes to the language. the first change were an experimental const
system. Later on features from numerous other languages were added to D2.
The problem with standard library was also solved by separating the runtime
from the standard library. In June 2010 Andrei Alexandrescu released his book
The D Programming Language and with the release of his book marked the
stabilization of D2, more commonly known today as D. The first version of the
D language D1 was discontinued in December 2012 and the only working version
is D2.
Syntax
The D language is a member of the C syntax family, resulting in very similar code
to C and C++. It’s a case sensitive language and does not allow punctuation
characters such as @, $ and % within the identifiers.
Some acceptable identifiers:
• testVariable
• _testvariable
• testvariable
• test_variable
• _testVariable1
Type Qualifiers
D has several type qualifiers. These may be applied both to local variables,
function arguments and return types. One is the familiar const which when
applied to an input variable signals that the function cannot modify it. Other
references to the same data may still be modified however. When applied to
a member-function (method) of an object, it signals that the “this” reference
is treated as const. Similarly, static, operates the same way as in C. The
immutable qualifier signals that not only is this particular reference to the
data immutable, the data itself is immutable and may not be changed anywhere
by anyone.
Variable Declaration
Declaration of local (or global variables) follows essentially the same syntax as
in C. They are declared in the usual way:
int i; // int
int *i; // pointer to int
int function(int) fp; // function pointer
int[3] i; // array of int
Arrays and function pointers may also be declared using C syntax, but this is
deprecated. All of the above declarations may be modified with the type qual-
ifiers mentioned above. Additionally, variables may be declared with dynamic
typing.
Arrays
One of the most popular inbuilt data structure known since C. In C you can
declare a static array like this:
int arr[10];
int[10] arr;
How you can define a static array in D? It’s simple, using either one of those1 .
Same goes with dynamic arrays, just that you don’t specify size in [] brackets,
leaving them empty.
1 However: You should avoid using the C approach, it’s less readable and kind of unique
Functions
There are however, several extensions to the familiar C syntax. First, there are
several additional keywords, or more accurately; storage classes, that may be
included in the declaration. These include the const and immutable described
above, which may be used to modify arguments or return types. On top of that
there are a couple of less familiar ones:
• scope - the qualified argument is not allowed to escape the scope of the
function.
• in - the same as const scope
• out - the argument is instantiated to its default value by the function
when it is called (i.e. it can be used as a C style “output argument”)
• lazy - arguments with this qualifier are evaluated by the called function,
rather than by the caller (see also Laziness below !needs link!)
• ref - signals that the argument must be passed by reference, or, when
applied to a return type, that the function returns a reference.
The inout qualifier is somewhat special; it acts as a kind of wildcard for storage
classes. A function declared as
where T is some type, will “transmit” its input qualifier to its return type.
Calling the above function with a const argument will result in a const return
type, immutable will result in immutable and so on. Function arguments and
return type may also be declared auto. See Type Inference below.
Pure Functions
D has support for “pure” functions using the pure keyword. Purity in D means
nearly what one would expect: pure functions may not do io or access any form
of global state, and may only call other pure functions. However, it diverges
from what one would expect in one respect: it does not prevent functions from
modifying mutable arguments. Pure functions are declared as
pure T fun(arguments){
// body
}
Classes
class MyClass
{
int a;
this(){
a = 3;
};
}
There are some obvious differences: Constructor are named this (although
they’re still invoked using the class name, as one would expect), field access
defaults to public, and there is no visibility control. That is, fields and methods
can be declared private, preventing their access and use from the outside, but
this does not hide them. The access parameters are as follows: public, private,
protected, package or export. There is also a synchronized keyword much like
the one in Java, but it cannot be applied to individual methods. The entire
class must be synchronized. Inheritance (and implementation of interfaces) is
specified with a simple colon:
D does not allow multiple inheritance, nor does it allow inheriting interface
implementation. The latter is rather different from the situation in Java, where
classes always inherit implementations of interfaces implemented in superclasses.
Class declarations may also include an invariant block, which puts constraints
on instances of the class.
Templates
D has C++ like templates. They are declared in the obvious way, with the
exception that D uses regular parenthesis instead of angle brackets for type
arguments. The following is an example of a simple copy template:
template TCopy(T)
{
void copy(out T to, T from)
{
to = from;
}
}
To use the above template, one must first instantiate it for a specific type, i.e:
int i;
TCopy!(int).copy(i, 15); // !(type) instantiates
Note also, the use of the out storage class to initialize and “return” the copy
using a C style “return argument”.
module multimedia.audio;
class Audio
{
// ...
}
To load the module and use it in a file called testProgram you simple type:
module testProgram;
import multimedia.audio;
void main()
{
auto audio = new Audio();
}
Features
Type Inference
While D is a statically typed C like language, it has support for a basic form of
type inference. This inference is invoked using the auto keyword, which may be
applied to values, function arguments and return types. For local variables the
syntax is as follows:
void main()
{
auto arr = [ 1, 2, 3.14, 5.1, 6 ];
For functions:
auto foo(int x)
{
return x + 3;
}
Function Overloading
Like in Java, the D language supports multiple functions with the same name
but with different parameter lists. Note that this is true both of methods on
objects, and on “free” functions. Certain restrictions are in place with regards
to overloading functions. An impure function cannot overload a pure function,
for instance.
Delegates
Lazy Evaluation
D has support for opt-in lazy evaluation of function arguments, which may
be invoked by explicitly wrapping input expressions in function literals or by
marking a variable with the lazy keyword. This keyword is essentially a storage
delegate of the kind suggested in the previous sentence. Simply put: a function
argument marked as lazy is evaluated by the called function, rather than caller.
So for example in the following code:
Contract Programming
Contract programming is a programming technique in which code correctness
is explicitly checked in the source code using preconditions, postconditions, in-
variants and other assertion-like structures. In D this is supported using the
assertion statments familiar to most programmers:
assert(expression);
These may be included anywhere in the code, and work in the usual way. If the
program is compiled for release however, they are ignored. Code should thus
never rely on them for effect.
In addition D also supports the rather more interesting pre/postcondition type
contracts. These allow the programmer to make assertions about what should be
true of the input (and possibly some global state of the program) of a function as
well as what should be true of its return values (or mutated values or global state)
immediately after it returns. Syntactically they are consist of the following
structure, inserted between the prototype of a function and its body:
The pre and postcondition clauses may contain arbitrary code, but should prefer-
ably be as brief and free of effects as possible since, much like assertions, they are
stripped out when compiling release targets. If the function has a void return
type the out clause should be declared without a result argument.
Lastly, invariants are postcondition-like clauses that may be attached to classes
and structs. These are called when the class or struct contructor exits, as well
as at the start of any public or exported method (after precondition). They can
also be forced by passing an object (or struct) with an invariant directly to an
assertion:
Invariants for classes are declared as follows (the syntax for structs is identical):
class MyClass
{
/*
members and
constructors
*/
invariant{
// contract code
}
}
Inline Assembler
D has support having assembly code mixed with its source code. This makes
it possible to create hardware specific code like drivers in D. This feature also
allows for great optimizing options in terms of performance. However this can
lead to D code that will not work on certain hardware setups.
Memory Management
In D you have three primary options for memory allocations in D Static data,
allocated in the default data segment Stack data, allocated on the CPU program
stack Garbage collected data, allocated dynamically on the garbage collection
heap.
D supports memory safe programming which means that if you choose as a
programmer to use the garbage collection that is present in the D language you
don’t have to be worried about memory leaks. This can be overridden and it is
possible to create and use classes with manual memory management.
Design Choices in D
According to the official webpage, the major design goals of D are to make a
safe, practical and portable C like language. Safe, both in the sense of memory
safety and in the sense of trying to guard against logical errors. Practical in
the sense that in as far as it is possible, writing good code and documentation
should be easy.
Writability
The language is, for the most part quite writable. Since it is a C/C++ like
multi-paradigm language, most styles of programming are accessable to the
programmer. This allows a D fluent programmer to adjust his or her style
depending on the problem domain. D makes this easy by including the obvious
OOP style structures and classes, as well as FP style closures, pure functions
(nearly; they may mutate arguments) and the passing (lazily even) of functions
or methods (via delegates) to other functions. Additionally D has C++ like
templates for meta-programming, a kind of type inference using auto, contract
programming for functions and classes, and even a specialized out argument
keyword, that disambiguates the use of C style “return arguments”. The one
big problem, of course, is that it isn’t entirely easy to keep track of all of these
frequently mutually unrelated (orthogonal even) features. One could easily end
up using only subsets of D.
Readability
Naturally, the inclusion of all these complicated features come with a readability
cost. To a programmer that has only worked with C, inspecting a D source file
and finding it riddled with qualifiers like immutable, export, lazy, inout,
auto and so on, is more than a little bit confusing. Aside from this explosion of
new keywords, the rest of the language is quite readable to anyone who has seen
C or Java source before. Most of the control structures are the same, variable
declaration (aside from the new qualifiers, and auto-typing) is the same, even
the foreach construct is very similar to its Java equivalent. Function declaration
is probably the most confusing area for newcomers to the language. Mainly
because of all the new keywords. Consider the following:
struct MyStruct
{
immutable int foo()
{ /* body */ }
immutable(int) bar()
{ /* body */ }
It is likely not clear to the newcomer, that foo() is immutable, bar() has an im-
mutable return value and that baz() is both immutable and has an immutable
return. And those are just zero argument functions with a single qualifier. Com-
plexity increases significantly if we want several arguments (or a variable number
of them) and more than one qualifier.
Reliability
D does a reasonable job with reliability. The inclusion of not only assertions, but
actual runtime contracts for functions and structs/classes is immensely helpful
in preventing logical errors. They are not a perfect cure all however, as they are
automatically stripped out when compiling for release.
Reliability is also helped by some of the aforementioned keywords; the out
keyword on function arguments certainly clarifies that the argument is C style
“return argument”. Similarly, qualifying arguments with in makes them read
only, and makes it illegal for them to escape the scope of the function. In
many cases, the proliferation of keywords and qualifiers in D aids reliability, at
the cost of readability, by allowing the programmer to specify clearly what a
particular value or function may and may not do. This is sort of similar in spirit
to the contract programming D also supports; the qualifiers act, in a sense, as
miniature contracts for a specific variable or function.
The readability problems mentioned above does hurt reliability a bit though.
The massive number of keywords, as well as the rather large set of paradigms
and features supported (this report isn’t long enough to cover anywhere near
all of them), is a potential problem for maintainability. As stated, the language
is large enough in features that knowing all of it is actually challenging. Clear
and consistent use of contracts can of course clarify the meaning of otherwise
unfamiliar coding conventions, but the feature explosion is nonetheless a prob-
lem.
First Program in D
The D language is a compiled language that needs a compiler to translate the
source code to machine code.
To create your first program in the language D you need to get one of their
compilers. The D Language have like many other languages several different
compilers that differ in:
• Reliability
• Popularity and Support
On the D language website its recommended for beginners to use the DMD
(Digital Mars D Compiler) as its implemented closest to the D language speci-
fication. Other compilers to look over is the GDC ( a GCC D compiler) or the
LDC (LLVM D compiler). Both GDC and LDC are open source compilers and
both support x86 and x64 architectures.
Running D code
To run a D source file you have two main approaches, either you compile your
source file as a script using rdmd (for the DMD compiler) and will only require
one step. Your second option is to compile the source code to an executable
(.exe in windows) and then run the executable to start your program.
Basic differences
hello world example, you can see the ln suffix. This you have probably already
seen in Java which adds new line character at the end of the string, whereas
writef is similar to printf in C where f suffix allows to use formatted data (%d,
%c, %f etc).
Code comments
and also one new type of comments is introduced, block comments within an-
other block comment:
/+ … /+ … +/ … +/
What does it mean? In “slash-asterisk” block comments you could not insert
one comment into another because the closing pattern */ was always closing
the outer opening pattern /*. So, for example, if you had code /*(a) … /*(b)
… */(c) … */(d) then the ‘a’ opening would be terminated at ‘c’ instead of
expected ‘d’ making d’s */ not being a part of the comment and causing syntax
error.
D makes code portability even easier. In C/C++ we can use #ifdef blocks
targeting some constant that exists only within header files (or compiler itself)
of specific type of operating system. For instance _WIN32 is a predefined in MS
Visual Studio (MSVC compiler). This wasn’t much of a help anyway because
we each operating system had its own header files (unless you were using cross-
platform compiler such as GCC) and some function names and/or arguments
were different. The crown example can be networking. In linux we use socket.h
and in Windows winsock.h. Their functions (at least in C) are quite different
from each other and you can’t write one code for both operating systems.
In D, however, you use much simpler approach. You can simply wrap code
within version(Windows) { … } or version(linux) { … } and you are good
to go! The code inside the brackets can be the same for both operating systems
and it still works, the compiler will take care of the rest. Beautiful isn’t it?
Well, you can say that Java or Python does the same. But hey, Java runs
virtual machine and loads tons of stuff just to print “hello world” (you are big
performance fan, aren’t you?) and python (as well as ruby, php or any other
version(development)
{
writeln(“This message is for development only”);
writeln(“Bug tracker located at: http://secret.ru”);
writeln(“This program is filled with bugs >_<”);
}
version(production)
{
writeln(“Welcome! This program is bug free as all our products”);
}