Book Image

Learning D

By : Michael Parker
Book Image

Learning D

By: Michael Parker

Overview of 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.
Table of Contents (19 chapters)
Learning D
Credits
Foreword
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

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.