Learning D

4.9 (11 reviews total)
By Michael Parker
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. How to Get a D in Programming

About this book

D is a modern programming language that is both powerful and efficient. It combines multiple paradigms in a way that opens up a whole new world of software design. It is used to develop both desktop and web applications, with future targets including mobile, and is available on multiple platforms. It is familiar to anyone with some experience in one or more of the C-family languages. However, hidden in the similarities are several differences that can be surprising when trying to apply common idioms from other languages. When learning D on your own, this can make it more time-consuming to master. In order to make the most of the language and become an idiomatic D programmer, it’s necessary to learn how to think in D.

This book familiarizes you with D from the ground up, with a heavy focus on helping you to avoid surprises so that you can take your D knowledge to the next level more quickly and painlessly.

Your journey begins with a taste of the language and the basics of compiling D programs with DMD, the reference D compiler developed by Digital Mars, and DUB, a community-developed build utility and package manager. You then set out on an exploration of major language features. This begins with the fundamentals of D, including built-in types, conditionals, loops and all of the basic building-blocks of a D program, followed by an examination of D’s object-oriented programming support. You’ll learn how these features differ from languages you may already be familiar with. Next up are D’s compile-time features, such as Compile-Time Function Evaluation and conditional compilation, then generic programming with templates. After that, you’ll learn the more advanced features of ranges and functional pipeline programming. To enhance your D experience, you are next taken on a tour of the D ecosystem and learn how to make D interact with C. Finally, you get a look at D web development using the vibe.d project and the book closes with some handy advice on where to go next.

Publication date:
November 2015
Publisher
Packt
Pages
464
ISBN
9781783552481

 

Chapter 1. How to Get a D in Programming

Before diving into the core features of the D programming language, some laying of groundwork is in order. This first chapter serves as a gentle introduction to D and as a guide to installing the prerequisite tools of the trade we'll be using throughout the book. The example program shown here isn't anything earth-shatteringly amazing, but it does demonstrate a couple of features of D that do not exist in other languages. The sample project that is developed over the course of the book is also introduced here, with a look at the motivation behind it and the features that will be implemented. Here's what you can expect to see:

  • Say hello to D: This examines a simple D program and gives you advice on where to go for help with D

  • The Digital Mars D compiler: This covers how to install and run DMD

  • Say hello to MovieMan: This is an introduction to the two versions of the sample project developed throughout the book

  • DUB—the D build tool and package manager: This covers how to install DUB and configure it to build the first version of MovieMan

 

Say hello to D


The D programming language is a multi-paradigm language that belongs to the C family. Out of the box, it supports aspects of procedural, object-oriented, generic, generative, and functional programming. That's not to say that it's an OOP language or a functional programming language, or that it can accurately be pigeonholed into any specific paradigm. The philosophy of D is to provide a range of tools that allow the programmer to have efficiency, control, and modeling power while ensuring that all of the disparate parts work smoothly together. Use object orientation where you need it, but forgo it for procedural or functional programming in other areas of your code base and be assured that it will all work together as a cohesive whole. Many D programmers will tell you that there isn't one specific feature of D that, taken in isolation, makes the language a pleasure to use. Rather, it's the sum of all the parts that keeps them writing D code.

New users coming to D from other C-family languages will find a great deal that looks familiar. That can be reassuring and makes for a good head start on learning some of D's features; caution is warranted, however. It's tempting to take that familiarity for granted and try to write D as if it were a more familiar language. For the most part, things will work as expected. D is, after all, a member of the C family. But in some cases, this is certain to lead to unexpected compiler errors, or the realization that some blocks of code aren't behaving in the manner the new D user thinks they should. In other cases, there is a more idiomatic D approach that can improve readability or maintainability, particularly when working with the standard library.

To learn a new language effectively, it's necessary to come at it with as close to a blank slate as possible. Just because features look the same on the surface as they do in another language, doesn't mean they are the same underneath. Given that premise, a consistent theme in this book is that D is not C++, Java, C#, or any language other than D. When you are writing D code, you should be thinking in D. Several of the code snippets in the book are intended not just to introduce D features, but to demonstrate explicitly how certain features differ from other languages. You are encouraged to enter such snippets into an editor yourself, especially if C++ is already in your muscle memory. Implementing the snippets as you encounter them and seeing the differences in action makes it more likely that you'll think in D instead of C++ when working on your own D projects.

Note

D started life as the Mars programming language, but Walter's friends kept calling it D. Eventually, he began to do so as well and the name stuck (see the D FAQ at http://dlang.org/faq.html#q1 for background). Neither name lends itself to productive web searches, but search engines are able to recognize the keyword dlang. Use that in your list of search terms rather than simply using D and you should be rewarded with a number of relevant hits.

An introductory program

This section presents a simple D program demonstrating a handful of language and library features. A brief description of each feature is then given, along with a reference to the chapter in which it is explained in more detail. Anyone familiar with C should be able to follow the code rather easily, though there might be a couple of features that seem unusual. If you happen to find any of it confusing, don't worry about it for now. Each feature will be explained later in the book.

In preparation for implementing all of the code snippets and examples in this book, it's a good idea to create a directory somewhere that's easy to navigate to from the command line—for example C:\LearningD or ~/learningd. However you choose to name this directory, it will be referred to as $LEARNINGD throughout the book. Each chapter should have its own subdirectory. The example in this section should be saved as $LEARNINGD/Chapter01/hello.d.

Note

Note that I prefer to use forward slashes when I type source paths, unless I'm talking specifically about Windows, which means Windows users should convert them to backslashes as needed.

Let's look at the example now:

import core.thread;
import std.stdio;
void main() {
    import std.range : iota, retro;
    write("Greeting in ");
    foreach(num; iota(1, 4).retro) {
        writef("%s...", num);
        stdout.flush();
        Thread.sleep(1.seconds);
    }
    writeln();
    writeln("Hello world!");
}

The first thing to understand is that all D source files serve a purpose beyond just being text files on your computer. A D source file is also a D module. Most D users use the terms source file and module interchangeably. A module is one of several levels of encapsulation in a D program (we'll talk more about encapsulation in Chapter 3, Programming Objects the D Way). If you compile a D source file named hello.d, then the compiler will read it into memory and your program will contain a single module named hello. This is the default behavior. Since we are implementing a simple one-module program, we'll just accept the default and put off learning how to override it until Chapter 2, Building a Foundation with D Fundamentals.

The first two lines of the example look like this:

import core.thread;
import std.stdio;

An import declaration tells the compiler to look up a given module, specified by the name immediately following the import keyword, and then make some or all of its symbols available within a specific section of the current module. In the format we use here, in which no symbols are specified, all publicly visible symbols in the imported module are made available. The location of the declaration is what determines the section, or scope, in which the symbols will be available. In this case, since the declarations are in module scope and no specific symbols are listed, the net effect is that every publicly visible symbol in the core.thread and std.stdio modules is available for use throughout the entirety of our hello module.

Consider the module name std.stdio. Each part of the name has a defined meaning. Although the term module name is used to refer to the full name, the part to the right of the dot, stdio, is the actual module name. The part to the left of the dot, std, is the name of a package. Packages are used to group modules into hierarchies. A module can belong to only one package, but that package can be a subpackage of another package, which can be a subpackage of another package, and so on. This means you can have multiple package names on the left side of the dot, such as mylib.data.formats.json. Here, we have three packages and are referring to a module called json. The package named formats is a subpackage of data, which is a subpackage of the top-level, or root, package called mylib. There's more to say about modules and import declarations in Chapter 2, Building a Foundation with D Fundamentals.

The std and core packages are available with any D compiler; the former is part of Phobos, the D standard library, and the latter is part of the runtime library, DRuntime. std.stdio makes available everything needed for basic file I/O, including reading from standard input (stdin) and writing to standard output (stdout). The module core.thread provides facilities for creating new threads and affecting the execution of the current thread.

Now take a look at the next line:

void main() {

Every D program requires a function named main. When a program is first launched, control passes from the operating system to the C runtime and from there to the D runtime. Finally, main is called and the program takes control. We'll look at this in a little more detail in Chapter 3, Programming Objects the D Way, when we'll also see that it's possible to execute code before main is called.

There are four fundamental alternatives for declaring a main function. Which one to choose is entirely dependent on the requirements of the program:

void main() {}
void main(string[] args) {}
int main() { return 0; }
int main(string[] args) { return 0; }

The first two versions are ultimately equivalent to the latter two; the compiler will ensure that they actually return 0 upon successful execution. Execution is considered to fail when an exception is thrown (exceptions are introduced in Chapter 3, Programming Objects the D Way). For most of the examples in this book, the first signature is all we need. Except for a couple of snippets later on, we aren't going to parse any command line arguments, so we can dispense with forms that accept an array of strings. We also aren't writing any programs that need to pass a return value to the OS on exit, so we have no need of the versions with the int return.

Note

Windows programmers might be wondering how D handles WinMain. DRuntime knows only of main, so if WinMain is used as the program entry point, then all of the initialization normally carried out by DRuntime must be handled manually. We'll learn more about DRuntime later.

Let's get back to the code. The next line is another import declaration, one which differs from the two declarations at the top of the module:

import std.range : iota, retro;

Because this declaration is inside a function, it is called a scoped import. Symbols made visible by scoped imports are only visible inside the scope in which the declaration is made. In this case, the symbols are visible only inside main. There's more to this declaration, though. Notice the colon, followed by iota and retro. In an import declaration, a colon followed by a comma-separated list of symbols means that only the listed symbols will be visible. In this case, no symbols from std.range are visible in main other than iota and retro. We'll see what they do shortly.

It's time for a line that actually puts something on the screen. For that, we're going to invoke a handy and very flexible function from the std.stdio module:

write("Greeting in ");

The write function is one of a handful of functions that print text strings to standard output. It's analogous to the C standard library function puts, but it differs in that it can take any number of arguments of any type. Each argument will be printed in the order they are given to the function, with no spaces added between them. For example:

write("Happy ", 100, "th birthday to", "you!")

This prints the text Happy 100th birthday to you!.

The next line introduces three items:

foreach(num; iota(1, 4).retro) {

The foreach loop is a loop construct that can be used to iterate over a range. iota is a function that returns a range of numbers, in this case from 1 to 3. retro is a function that takes a range as input and returns a new one containing the elements of the original range in reverse order. The ultimate result of this line is a loop iterating over the numbers 3, 2, 1. The foreach loop is described in Chapter 2, Building a Foundation with D Fundamentals. The entirety of Chapter 6, Understanding Ranges, is devoted to explaining ranges, an integral part of D. Both the iota and retro functions are described in Chapter 7, Composing Functional Pipelines with Algorithms and Ranges.

It's worth noting here that iota(1, 4).retro is the same as retro(iota(1, 4)). The former syntax is possible because of a feature called Uniform Function Call Syntax (UFCS). Given a function func and a function argument arg, func(arg) can be written as arg.func(). You'll learn more about UFCS in Chapter 2, Building a Foundation with D Fundamentals.

Next up are the three lines of the foreach loop:

writef("%s...", num);
stdout.flush();
Thread.sleep(1.seconds);

The writef function is a variation of write that prints a formatted text string to standard output. It's analogous to the C standard library function printf; with .stdout is a global instance of a type called File, both of which are declared in std.stdio.

When writing to a file handle, the operating system buffers text internally for efficiency. Normally, when the handle belongs to a console or terminal, line buffering is enabled. This means that the buffer is flushed when a newline character is printed to the output stream. In this example, calling flush manually flushes the buffer in order to achieve the effect of having one number printed per second; otherwise, it would all be printed at once after the loop exits and the first call to writeln executes. This effect is regulated by the call to Thread.sleep, which causes execution of the process to pause for one second.

Note that the call to Thread.sleep is not using UFCS. Thread is a class, and sleep is a static member function. 1.seconds, however, does use UFCS. The function seconds is declared in a runtime module named core.time. This module is imported indirectly by core.thread such that all of its symbols are visible. 1.seconds is the same as seconds(1) (parentheses on function calls are sometimes optional). This function returns an instance of the Duration type, which sleep uses to determine how long to pause the current thread. Public imports and function call syntax are discussed in Chapter 2, Building a Foundation with D Fundamentals. Classes and member functions are introduced in Chapter 3, Programming Objects the D Way.

Finally, the last two lines of the example:

writeln();
writeln("Hello world!");    

The writeln function is identical to write, but has one additional feature: it appends a newline character to the output. Here, we call it twice. The first call appends a newline to the text that was written in the loop, while the second prints the greeting. This could be condensed to one line as writeln("\nHello world!"). Note that there is also a formatting version of this function called writefln.

In order to verify that this program works as expected, it will need to be compiled and executed. Instructions on how to do so will be discussed later in the chapter.

Getting help

In your journey with D, you're inevitably going to need assistance. There are a couple of primary online locales where experienced D users can be found answering questions from not-so-experienced D users and having fierce debates about language features, as passionate programmers are known to do.

The first place any new D user should look is http://forum.dlang.org/. This isn't a self-contained forum as the URL implies, but rather a web interface to a newsgroup server maintained by Digital Mars. If you ever find yourself wondering why you can't edit or delete posts in the D forums, this is why. The forum targeting new users is digitalmars.D.learn, project and major news announcements are made in digitalmars.D.announce, while digitalmars.D is where you can go to witness or participate in discussions about the state of the language and its future direction. As you become more familiar with D and its ecosystem, some of the other forums might start to be of interest to you.

Note

The web interface called DFeed was developed in D by an active community member named Vladimir Panteleev. You can find the source for DFeed at https://github.com/CyberShadow/DFeed.

If the web interface doesn't do it for you, there are other options to access the forums. Given that the primary backend is a newsgroup, you can set up an account in a newsgroup reader for news.digitalmars.com and select the newsgroups you're interested in following. Alternatively, you can point your browser at http://lists.puremagic.com/mailman/listinfo and subscribe to forums of interest via the mailing list interface. Again, the mailing lists are collectively an alternative interface to the newsgroups and not a completely independent entity.

Tip

The D community is generally helpful and friendly to those asking questions in the digitalmars.D.learn forum. You should never feel hesitant about asking questions in the D forums. Experienced users drop by regularly, willing to answer the most basic questions. You can also find a number of D users idling in the #D IRC channel. If you have an IRC client, #D is located at http://freenode.net/. Anyone there can answer your questions about D. I've never been much of an IRC user, but I do drop by #D now and again. Whenever I'm around, I'll be happy to answer questions about this book or any of my other D projects. I'm usually found under the handle aldacron in IRC and my real name in the forums.

 

The Digital Mars D compiler


DMD is the reference compiler for the D programming language. Created by Walter Bright, it is still maintained by him with the help of a handful of talented volunteers. It's not the only D compiler out there. GDC is built on top of the GNU Compiler Collection (GCC) and LDC uses the LLVM toolchain. Both compilers were created, and continue to be maintained, by members of the D community. As you write more D code, you'll find that DMD has blazingly fast compile times, while GDC and LDC produce highly optimized executables that tend to have better performance (though they are far from slow in terms of compilation speed). Because of this, it's not uncommon for D programmers to use DMD during development of a new project to take advantage of the faster compile times, then use one of the other compilers to build the final release and benefit from the more advanced optimizations. That said, we're going to be focused exclusively on DMD in this book. The code snippets and the sample project should compile with GDC or LDC just fine, but any compiler-specific instructions in the text will be for DMD.

There is nothing complex about installing DMD. Before we make that happen, it's important to understand a couple of things about how DMD works.

Frontends, backends, and linkers

One of the primary goals Walter established during D's early development was that it must be binary-compatible with C. Essentially, this means that it should be possible to compile a C source file with a C compiler and combine the output into a program that is written in D and compiled with a D compiler, or vice versa. To achieve this goal, output from a D compiler has to be in a format that C toolchains can understand. An in-depth discussion of compiler technology is quite a long way beyond the scope of this book, but it's necessary to have a minimal understanding of a small part of it in order to make DMD work for you more effectively.

Compilers typically have two major components that work together to create the final output: the frontend and the backend. The frontend is tied directly to a specific language. It takes source code as input and transforms it into an intermediate, language-agnostic format as output. The backend is tied to a specific platform. It takes the transformed code from the frontend as input and generates machine code as output, typically in the form of object files. Once the object files are created, ultimately one of two things will happen: either they are passed to a tool that links them into a final executable or they are passed to a tool that packs them into a library. The former tool is called a linker and the latter is called a librarian or archiver, though it's possible for one tool to perform both tasks.

For Walter to achieve his goal of binary compatibility with C, he opted to make use of existing toolchains where possible. This way, he could expend most of his effort on the frontend, the part that actually deals with D source code, and leave the rest to existing, tried-and-tested tools. He was already in the C and C++ compiler business—the company he owns, Digital Mars, distributes the Digital Mars C and C++ Compiler (DMC)—so he had his own existing backend and linker sitting on his hard drive. Appropriately, he began to implement a D frontend and hooked it up with the DMC backend to create DMD. Since the Digital Mars C(++) toolchain is Windows-specific, other options needed to be considered when it came time to port DMD to additional platforms. Walter's solution was to modify the backend to generate the appropriate output for each target platform, then have the compiler make use of each platform's system linker and librarian to generate the final binaries.

Running DMD on POSIX systems is a straightforward process. Since the compiler uses the system toolchain to create the final output on each system it supports, things tend to work smoothly without any conflicts. This includes support for both 32- and 64-bit output. The story on Windows isn't quite so rosy.

Note

In this book, POSIX is used to describe any non-Windows, Unix-like system supported by DMD, whether they are 100 percent POSIX-certified or not. That includes Linux, Mac OS X, and the various BSDs.

The problem is that the Digital Mars linker, OPTLINK, is ancient. It uses an object file format that is incompatible with most modern C and C++ toolchains. We'll explore this issue in more detail in Chapter 9, Connecting D with C, because it's an important thing to understand when interacting with C. Another point about OPTLINK is that it only supports 32-bit output. 64-bit support was implemented on Windows by giving DMD the ability to generate object files in a format understood by the Microsoft toolchain.

The major upside of this approach is that it eliminates the annoyances that come from conflicting object file formats on Windows, at least when compiling 64-bit binaries with the -m64 command line switch. The downside is that compiling 64-bit binaries with DMD now requires the installation either of a version of Windows SDK that includes the MS tools, or a non-Express version of Microsoft Visual Studio. DMD cannot be a completely self-contained distribution for 64-bit development on Windows. As of DMD 2.067, the Microsoft tools can also be used in place of the default Digital Mars toolchain to generate 32-bit binaries by passing a command line switch, -m32mscoff, to DMD.

Installing DMD

Installing the very latest release of DMD is always dead easy. Simply point your browser to http://dlang.org/download.html and pick your poison. Several options are available, including an installer for Windows, a DMG file for Mac OS X, deb packages for Ubuntu and Debian, RPM files for Fedora, CentOS and openSuse, and ZIP files for each of those platforms plus FreeBSD.

DMD is also available from the package repositories of most Linux systems, as well as through homebrew and macports for OS X users and the ports tree for BSD systems. If it isn't available in your platform's package repository, or if it's outdated, choose the appropriate package for your platform from the download page. The ZIP file is a good choice when you want more control over where the compiler is installed. Here, we're going to note a couple of specific points related to the Windows installer, then we'll see how to install from the ZIP file.

The Windows installer

Either a version of the Windows SDK that ships with the Microsoft linker or a non-Express version of Microsoft Visual Studio must be installed in order to compile 64-bit apps (or to use the -m32mscoff option) with DMD on Windows. It will help a great deal if one of these is installed before installing DMD. This way, the DMD installer can automatically find where the MS tools are located and configure the compiler to use them. If DMD is installed first, then its configuration file, sc.ini, must be manually edited for it to find them.

Note

Older versions of the Windows SDK shipped with a complete compiler toolchain. Since the release of the Windows 8 SDK, that is no longer the case. The Express versions of Visual Studio include the toolchain, but do not ship with the system libraries needed by DMD. Installing the Windows 8, 8.1, or 10 SDKs with a version of Visual Studio Express will provide everything necessary. When using an older version of the SDK, no additional software needs to be installed.

A better option is to install a non-Express version of Visual Studio. The Community versions are available for free. These include everything DMD needs in one package. Support for VS 2015 was added in DMD 2.069.

The DMD installer will also ask if you want to install Visual D. This is a Visual Studio plugin developed by Rainer Schuetze. While I strongly recommend that you use a text editor and the command line to work through the code samples in this book in order to better familiarize yourself with DMD, you might find Visual D quite useful later on, especially if you enjoy using Visual Studio.

Installing from the ZIP

Installing from the ZIP file isn't difficult, but requires a bit of manual manipulation for things to work easily from the command line. The required steps differ across platforms. We'll start with how to install from the zip on Windows before looking at installation on the supported POSIX platforms.

The Windows ZIP

Unzip the files into any directory. I recommend something short and easy to remember, such as C:\D (this is the default location used by the installer). Optionally, add the windows\bin subdirectory to the system PATH. Assuming you've unzipped in C:\D, the full path would be C:\D\dmd2\windows\bin.

The Windows version of DMD is distributed as a 32-bit program and can compile both 32-bit and 64-bit binaries. By default, it is configured to compile 32-bit using OPTLINK. If you're happy with that, there's nothing else to do. 64-bit binaries can be compiled by passing -m64 on the command line when invoking DMD but, as described, the MS toolchain will have to be installed for it to work. After the MS tools are installed, open the C:\D\dmd2\windows\bin\sc.ini file in a text editor. There, you'll find a block of instructions on how to configure DMD to use the Microsoft tools. Several of the standard Windows SDK and Visual Studio paths are already preconfigured, with most of them commented out. If the default, uncommented paths do not match your version of the Microsoft toolchain, it should be enough to comment out those lines by adding a semicolon, ;, in front of each and uncommenting the lines that match your installation by deleting the existing semicolons from in front of them. At this point, I'd like to remind you that the DMD installer takes care of all of this for you.

Note

On every supported platform except Windows, the DMD configuration file is called dmd.conf. On Windows, it's called sc.ini to maintain compatibility with the Digital Mars toolchain that DMD uses by default.

The POSIX ZIPs

On the three POSIX systems supported by DMD (Mac OS X, Linux, and FreeBSD), it's not uncommon to extract the ZIP in the home directory. This will result in a ~/dmd2 directory. The easiest thing to do with this is to leave the binaries where they are and add the appropriate subdirectory to the system PATH variable.

The Linux ZIP file contains both 32-bit and 64-bit versions of DMD; the Mac OS X ZIP file contains a 64-bit version of DMD; for FreeBSD, there are two ZIP files, one for 32-bit and one for 64-bit DMD. Choose the appropriate path from among the following to add to your system PATH variable:

  • ~/dmd2/linux/bin32

  • ~/dmd2/linux/bin64

  • ~/dmd2/osx/bin

  • ~/dmd2/freebsd/bin32

  • ~/dmd2/freebsd/bin64

A 32-bit DMD will compile 32-bit apps by default, and a 64-bit DMD will compile 64-bit by default. For all versions of DMD, the command line flag -m32 will force 32-bit compilation and -m64 will force 64-bit compilation.

Compiling the example

With all of that background out of the way, it's time to get down to business. Open a command prompt and navigate to the directory where you saved the example. If you followed my advice, that should be $LEARNINGD/Chapter01. Now execute the following command:

dmd

If the PATH variable is properly configured, you should see the compiler print a header containing copyright information and the compiler version, followed by a list of supported command line options and a brief description of each (you might need to scroll up to see all of it). Now try this:

dmd hello.d

If you see errors or are unable to get DMD to run, head to the digitalmars.D.learn forum introduced earlier and ask for help. Otherwise, you should find yourself the proud parent of a brand new executable binary, called either hello or hello.exe, in the current directory. This is what its output looks like for me from the Windows Command Prompt:

You've now successfully compiled and executed a D program. It took you two commands to do so, but it doesn't have to be that way. DMD has a number of useful command line options, some of which are tied closely to language features. Other options are independent of language features and instead control the location or format of the final output, or instruct the compiler to perform a specific task. One such option, -run, is useful for trying out short examples like the ones you'll see in this book.

When DMD is executed with the -run option, it creates an executable in the current directory as normal. The difference is that -run causes the compiler to immediately execute the program and then delete the executable after the program exits. Try it on the hello.d example and see. This will save you from the execution step every time you want to compile and run an example.

Documentation and source code

In addition to several useful tools, the DMD distribution ships with some HTML documentation and the source code for the compiler, DRuntime, and Phobos. Both docs and source can be beneficial even after becoming familiar with D, but especially so while learning it.

The documentation

In the root directory where you installed or unzipped DMD, you'll find a subdirectory called html. Open the html/d/index.html file in a web browser and you'll see the front page of the documentation for your version of the compiler. The documentation for the latest compiler release is also available online at http://dlang.org/. In the navigation bar on the left-hand side of the page, as seen in the following screenshot, look for the links named D Reference and Standard Library; the former is the place to look up information about language features, the latter is the documentation for the DRuntime and Phobos libraries:

Now and again, you might find that the documentation for a function or a language feature isn't clear. Sometimes, it isn't as descriptive as it could be, or refers to functionality that has changed. Keep in mind that D is a community-driven language that relies on users to help by reporting problems or contributing fixes. If you'd like to contribute, documentation fixes can be submitted as pull requests at https://github.com/D-Programming-Language/dlang.org, and bug reports can be filed at https://issues.dlang.org/ under the websites component. Once that's out of the way, you still need to solve the problem that caused you to look in the docs in the first place. You can do that by visiting the forums and asking for help, or often by going right to the source.

The source

At the same level as the html directory lives another directory labelled src. Open it up and you'll see subdirectories called dmd, druntime, and phobos. In each is the source code for the eponymous project. I don't expect readers of this book to start modifying the compiler just yet, but you might want to read the files src/dmd/readme.txt and src/dmd/backendlicense.txt to understand the licenses of the DMD frontend and backend source. More immediately useful is the source for DRuntime and Phobos, which is all released under the Boost Software License.

As you're learning D, taking a peek at a function's implementation can help clarify its usage if the documentation comes up short. Later, studying the Phobos source can be a great way to learn some D idioms and other useful tricks. At some point in your D career, you might be interested in exploring what DRuntime does under the hood to manage your program. In the beginning, you'll likely find more benefit from the source in the core package. core is the primary DRuntime package, the part of the runtime that users directly interact with. We won't spend a lot of time on core in this book, but there will be brief discussions about a few of the modules it exposes as the need arises.

The source that ships with each DMD release is the source for that specific version of the compiler. Once a new version is out the door, development continues toward the next release. All such development happens at GitHub, specifically in the D Programming Language organization at https://github.com/D-Programming-Language. Here, you can find projects for the compiler, Phobos, Druntime, the website, and several tools and utilities, including the Windows installer, Visual D, and DUB (which we'll look at shortly). If you enjoy contributing to open source projects and also enjoy using D, there are many ways to contribute aside from committing your own source modifications. The quickest way to get started is to report bugs at https://issues.dlang.org/, or to review contributions at GitHub. The more users contribute, the faster the language moves forward.

 

Say hello to MovieMan


Example code and snippets are quite suited to the role they play in a book such as this: highlighting the usage of a language feature in the context of an explanation about that feature. What they aren't ideal for is showing the big picture, or reinforcing how all the disparate language features work together. That role is best played by a sample project that is developed over the course of the book.

In deciding what sort of program to develop as the sample project, I had a few goals in mind. Most importantly, it should be easy for the reader to follow, shouldn't be too time-consuming to implement, shouldn't have any third-party library dependencies, and should serve as a demonstration of as many D features as possible. I finally settled on something that I think fits each requirement to some degree. Few readers will find it immediately useful in its final form, but that's no problem. A practical post-book D project is to modify or extend the sample to tailor it to a specific, customized use case.

The problem

A few months before I started on this book, my wife and I moved house. Though I rarely buy new DVDs these days, I've assembled a modest collection of them over the years. I was more than a little obsessive about keeping them in their original cases, so I kept them lined up on a number of shelves, loosely organized around genre or leading actor/actress. That is just untenable in the long run. They take up too much space and they're a pain to pack when relocating. Even so, I'm reluctant to rip them to cloud storage and throw them all out. Motivated by the move, I bought several multi-disc cases (the kind with a zipper on the outside and several CD/DVD sleeves on the inside) and transferred each disc from its original case into one of the new ones.

This was something I just wanted to get done, so I only made a meager effort to organize the cases. The result is that it's no easy thing to find a particular DVD. Since the move, I've had it in the back of my mind to whip up a program I can use to organize things, but I've continually put it off as it isn't a big priority. This book is the perfect excuse to get it done. So you and I are going to create a program that can allow me to organize my DVDs. I'm going to call it MovieMan (for Movie Manager) because that sounds much better to me than DVDMan.

The features

We're going to develop two versions of the program. In order to keep the size and complexity of the program manageable, the first version will be a command-line program with a limited number of features. The user will be able to:

  • Enter a movie title, a case ID, and a page (sleeve) number

  • Find and display movies by title

  • List all movies in a given case

  • List all movies on a given page of a given case

  • List all movies

We'll implement this first version piece by piece, adding new functionality as we learn about the language features we'll use to implement it. We'll complete the program in Chapter 7, Composing Functional Pipelines with Algorithms and Ranges.

In Chapter 10, Taking D Online, we'll spend the entire chapter developing a different version of the program as a web application using a library called vibe.d. The purpose of this version isn't to demonstrate language features, but to show how to develop web apps with vibe.d. We'll relax our requirement about third-party libraries (vibe.d is a library, after all) and make use of a database API to store the movie data. We'll also add support for editing movies and deleting them from the database. By the end of the book, you'll have two fairly simple programs that illustrate how several D features work together.

 

DUB – the D build tool and package manager


There is nothing challenging about compiling simple D programs with DMD on the command line, but things can get a bit out of hand as a project grows. A complex D program will be composed of numerous modules and can be dependent on several third-party libraries. There are a number of approaches that users have taken to compile their D projects. Some use makefiles or existing tools that have some form of support for D, such as CMake or SCons, and a few have even created their own build tools from scratch. For several years, I used a custom build script written in D for most of my projects.

In the past, having so many different approaches for building D code could sometimes be a bit of an annoyance for users of D libraries, particularly when using multiple libraries that each had a different build system. Users of a library I maintain often asked me to add support for different build systems to better match their own workflow, something I was extremely reluctant to do since learning, implementing, and maintaining such support would cut into my already scarce free time. Thankfully, this is one growing pain that the D community has left behind. In this section, I'm going to introduce you to DUB, which has fast become a central part of the D ecosystem.

Created by Sönke Ludwig, DUB provides a unified approach to building D projects. To facilitate this, it also serves as a distribution platform. Anyone with a D library can share it with the world by creating a DUB configuration for it and registering it in the DUB registry. Subsequently, any DUB user can use the library by adding a single line to her project's DUB configuration file. It can also be used to easily distribute open source executables. Potential users can use one DUB command to download, compile, and run a D program.

In Chapter 8, Exploring the Wide World of D, we're going to look at the DUB registry and see how to use DUB-enabled libraries in your own projects as we take a tour of the D ecosystem. Now, we're going to set up a DUB configuration specifically for MovieMan, but the same steps can be applied to any new project you create.

Getting started

DUB is available in most of the common package repositories and the latest release can always be found at http://code.dlang.org/download. Download the appropriate tarball, installer, or ZIP file for your platform. Installing from ZIP or tarball generally requires no special steps beyond unzipping the files to a directory of your choice (such as C:\dub or ~\dub) and ensuring the directory is on your PATH. POSIX systems will also need to have some prerequisites installed, so be sure to read the instructions on the download page. The Windows installer and ZIP both come bundled with all dependencies.

Once the program is installed and PATH is properly configured, open a command prompt and type the following command:

dub -h

If all goes well, you'll see a list of commands and command-line options (you will likely have to scroll up to see the whole thing). The command-line options are generic, meaning they can be passed to dub with any DUB command.

DUB usage revolves around commands. The default behavior when no commands are specified is to build and run any DUB project in the current directory. Unlike command-line options, DUB commands have no preceding dash. You can get help on any DUB command by passing its name to dub followed by -h or --help (the former is a synonym for the latter). For example, to get help with the build command, type the following:

dub build -h

This will print a brief description of the command, along with any command-specific arguments it accepts, followed by the same list of common command-line options you saw with dub -h. Much of what you need to know to get started with DUB on the command line can be gleaned from using the -h option. If you get stuck, you can ask for help in the DUB forums at http://forum.rejectedsoftware.com/groups/rejectedsoftware.dub/.

Configuring the MovieMan project

It's possible to set up a new project by hand, but DUB can set up a common directory structure and an initial configuration with a simple command called init. By default, with no arguments, init will create a new project using the current working directory as the project root and the name of that directory (not the full path) as the project name. If an argument is given to init, DUB will use it as the project name and create a new subdirectory of the same name in the current directory. Let's do that now for MovieMan.

Navigate to $LEARNINGD/Chapter01 and execute the following command:

dub init MovieMan

Listing the contents of the MovieMan directory shows the following:

09/20/2015  12:31 PM    <DIR>          .
09/20/2015  12:31 PM    <DIR>          ..
09/20/2015  12:31 PM                38 .gitignore
09/20/2015  12:31 PM               120 dub.sdl
09/20/2015  12:31 PM    <DIR>          source

DUB has generated three files and one subdirectory in the project directory tree. You can see two of the files in the preceding output. The third file, app.d, is inside the source subdirectory.

DUB is tightly integrated with Git, a distributed source control system. The .gitignore file is used to tell Git to pay no attention to files whose names match certain patterns. DUB is helping out by generating this file and including some file types that aren't commonly included in source control, but that might be created as part of the build process. For our purposes, we can ignore .gitignore.

By default, when running DUB in a project directory, it searches for the project source files in the source subdirectory. Also by default, it expects to find the source/app.d file. If this file exists, an executable will be built; if not, DUB compiles a library. The app.d that dub init generates for us isn't empty. It contains a main function that prints a line of text to the screen.

Sometimes, you might want to add DUB support to an existing project where it might be problematic to change the directory structure or rename the main module to app.d, or perhaps you just don't want to follow the convention that DUB expects. No matter your reasons, all of the defaults in the preceding paragraph can be overridden in the configuration file, dub.sdl. As you gain more experience with DUB, you'll learn how to go beyond the simple configurations we use in this book.

Understanding dub.sdl

DUB supports two formats for its project configuration files: Simple Declarative Language (SDLang) and JavaScript Object Notation (JSON). The former is the default format output by the init command (the command dub init -fjson will create dub.json instead). Originally, JSON was the only format supported. As of DUB 0.9.24, SDLang is the preferred format, though JSON will be supported indefinitely. Go to http://semitwist.com/sdl-mirror/Language+Guide.html for more about SDLang and http://json.org/ for JSON.

DUB requires a configuration file to be present in any project it is intended to manage. It supports a number of predefined fields for defining metadata that provide information about the project, such as its name and description, and instructions for DUB to follow when building the project. The documentation at http://code.dlang.org/package-format is the place to visit to keep informed on supported DUB configuration options.

Note

You've already seen the term package in reference to a hierarchical grouping of D modules. It's common to use the term DUB package to refer to a DUB project.

The configuration generated by the init command is entirely metadata. The metadata in a DUB package configuration comprises the first few lines by convention. None of the entries are required to be in any particular order. Take a look at the complete contents of the dub.sdl file generated by dub init for the MovieMan project:

name "movieman"
description "A minimal D application."
copyright "Copyright © 2015, Mike Parker" authors "Mike Parker"

Every DUB package must have a name that can be used as a unique identifier. The name should be comprised of lowercase alphanumeric characters. Dashes and underscores are also permitted. When generating a project, DUB will always use lowercase letters. If the configuration file is manually edited to include uppercase characters in the package name, DUB will internally convert them to lowercase when it loads the file.

The name field is the only one that is required. The other fields can be deleted and the project will build just fine. However, if there is any intention to register the package with the online DUB registry, then it's a good idea to always include the generated metadata fields at a minimum. There are other useful metadata fields that are not generated by default, such as license and homepage. The more metadata provided, the more potential users can learn about the project from the DUB registry.

Most of the metadata items are only visible from the package-specific page in the registry. The name and description fields are the only two that are displayed in the list of all registered packages. As such, the description shouldn't be very long or overly vague. What you want is a short, succinct summary of the package so that anyone browsing the registry can determine whether it's something he might be interested in. We aren't going to register MovieMan in the DUB registry, but if we were, then we might use something like the following to describe it:

description "A command-line DVD database."

The name used in the copyright and authors fields for a generated dub.json comes from the name of the user account under which dub init was executed. Multiple authors can be listed as follows:

authors "Bjarne Gosling" "Brian Pike"

Several configuration fields recognized by DUB require a list of values.

The majority of DUB directives are not simply metadata, but they directly influence the build process. There are directives for specifying library dependencies, compiler flags, which compiler to use, and more. We'll add one to the MovieMan configuration shortly.

Building and running MovieMan

DUB allows you to build a project and run the resulting executable in two separate steps, or to do it all at once. To build the project without running it, you use the aptly-named build command. To both build and execute the project, you can use the run command, which is the default behavior when invoking dub without specifying any commands (so both dub and dub run are the same thing).

Let's give DUB a go at building the project, first by specifying the build command. Here's what I see on my system:

C:\LearningD\Chapter01\MovieMan>dub build
Building movieman ~master configuration "application", build type debug.
Compiling using dmd...
Linking...

You can see that, by default, DUB isn't very verbose about what it's doing. It tells you which build configuration it's building and the build type, lets you know when it starts compiling and which compiler it's using, and then tells you that it's creating (linking) the executable. The build configuration, build type, and compiler can all be specified in the configuration file.

Now that the executable is built, it's just sitting there waiting to be executed. Let's make use of the run command to do so:

C:\LearningD\Chapter01\MovieMan>dub run
Target movieman ~master is up to date. Use --force to rebuild.
Running .\movieman.exe
Edit source/app.d to start your project.

If you haven't yet examined the generated app.d in your text editor, it might not be immediately obvious to you that the last line is the actual output of the generated code and the previous lines are from DUB itself. Look at the first line of output in particular. This is telling you that the executable is already built and that, if you want to build it again, you should use the --force option to do so. That might seem like an odd message to see when running the program. To understand it, try running the build command again:

C:\LearningD\Chapter01\MovieMan>dub build
Target movieman ~master is up to date. Use --force to rebuild.

As you can see, DUB refused to build the project and is telling you why and what to do. Try again, this time with the --force option and it will build successfully. Also, see what happens when you invoke dub and dub --force.

So the take away here is that DUB isn't going to rebuild your project if it is already up-to-date and it will always check whether a rebuild is needed before executing the program. We can define "up-to-date" to mean that the timestamp of the executable is more recent than the last modification time of any of the source files. If you edit any source file in the source directory, then trying to build again will succeed. To demonstrate that, let's edit app.d to look like the following:

import std.stdio;
void main() {
  writeln("Hi! My name is MovieMan.");
}

Now, executing either dub or dub run will cause DUB to notice that app.d has a more recent timestamp than the executable. In response, it will rebuild the application before executing it.

Tip

To build or not to build

The build command comes in handy when you invoke it as you add new code to a project. I like to do so after adding several new lines, or a new function, just to make sure everything still compiles. DMD is so fast that, even as a project gets larger, this does not impede development. Only when I've completed adding or modifying a feature do I invoke dub run to make sure the app is working as expected.

Changing the output directory

Executable programs often ship with a number of resources. These generally should be located either in the same directory as the executable or in subdirectories. By default, the binary that DUB builds, whether it is a library or an executable, is output to the same directory in which the dub.json file resides (which we can call the project or package root). When there are resources to worry about, this can look a little cluttered. Even without resources, I prefer my binaries to be written in their own directory. This can be accomplished with one simple addition to dub.sdl:

targetPath "bin"

targetPath is a build directive that tells DUB where to write the binary. It can be placed anywhere in the file, but convention dictates that it follow the metadata. Here, I've used a relative path that will cause DUB to create a subdirectory named bin (if it doesn't already exist) and write the binary there. Absolute paths, such as C:\dev\MovieMan or /~/bin/MovieMan, are also acceptable. However, you should prefer relative paths to keep your DUB packages completely self-contained if you are going to distribute them.

 

Summary


In this chapter, you've taken an introductory look at the D programming language, including a line-by-line breakdown of a simple D source file. You've learned how to install and run DMD, the Digital Mars D Compiler. You've become acquainted with the sample project you'll be working on throughout this book, called MovieMan, by looking at the feature requirements of the two different versions you'll create. Finally, you've had an overview of DUB and how you'll use it to manage the sample project.

Now that you know enough about the tools to get some work done, we can dig into the language and start using it in the next chapter. There, we'll look at the basic features that form the building blocks of any D program.

About the Author

  • Michael Parker

    Michael Parker created the popular D library collection, Derelict, in 2004, and has been the primary maintainer ever since. He also blogs about the language at The One with D, and is a coauthor of the book Learn to Tango with D, Apress. He lives in Seoul, South Korea, with his wife, Mi Kyoung, and their dogs, Charlie, Happy, Joey, and Toby.

    Browse publications by this author

Latest Reviews

(11 reviews total)
Loving the book. Not too hard and reads perfectly.
fast and satisfying to buy here
Well organised and well written. A good start for would-be D programmers.

Recommended For You

D Web Development

Leverage the power of D and the vibe.d framework to develop web applications that are incredibly fast

By Kai Nacke