# Hands-On DLang ## Setup ### Installing DMD and DUB #### OS X ##### Installing with Homebrew (recommended) ```bash brew install dmd brew install dub ``` ##### Installing locally using the install script ```bash curl -fsS https://dlang.org/install.sh | bash -s dmd echo "~/.dlang/dmd-2.079.0/activate" >> ~/.profile # Add dmd and dub to PATH on starting a bash shell ``` ##### Installing using the installer * Download http://downloads.dlang.org/releases/2.x/2.079.0/dmd.2.079.0.dmg. * Open `dmd.2.079.0.dmg` * Run `DMD2.pkg` (you might need to activate the “allow installing applications from unverified developers” option in your security settings) and install with the default settings. #### Windows * Download http://downloads.dlang.org/releases/2.x/2.079.0/dmd-2.079.0.exe. * Run `dmd-2.079.0.exe` and install with the default settings (this will also install Visual Studio if you do not have it installed yet). ### Recommended editor setup Visual Studio Code is the recommended editor, because it has the best D integration at the moment. If you want to use another editor or IDE, that is perfectly fine. However, instructions will only be provided for Visual Studio Code. #### Installation of Visual Studio Code Download and install Visual Studio Code from here: https://code.visualstudio.com/. OS X users can also install it using Homebrew: ```bash brew tap caskroom/cask brew cask install visual-studio-code ``` #### Extension setup * Open the Extension view in the sidebar: |Operating system|Shortcut | |----------------|---------| |OS X |⌘ + ⇧ + X| |Windows |⌃ + ⇧ + X| * Install the extension “D Programming Language (code-d)” (requires that git is installed). * Restart Visual Studio Code. ## Basics ### Hello World ```D import std.stdio; void main() { writeln("Hello World"); } ``` ### Imports and modules D has the concept of _modules_ and _packages_. By importing a certain module with the `import` statement, all public symbols from module become available. The standard library, called Phobos, is located in the `std` package. E.g. in order to import the `file` module from Phobos, you would write: ```D import std.file; ``` #### Selective imports It is possible (and often good style) to import symbols selectively from a module: ```D import std.stdio: writeln, writefln; ``` #### Scoped imports It is not necessary to place imports at the beginning of a file. They can be located anywhere in the code. If they appear inside a certain scope (delimited by braces), the imported symbols are only available inside that scope. Here is an alternative version of the hello world program: ```D void main() { import std.stdio: writeln; writeln("Hello World"); } /* writeln is not available outside of the main function */ ``` #### Imports match files and directories The module system is entirely based on files. E.g. `my.thing` refers to a file `thing.d` in the folder `my/`. ### Basic Types D has the following basic types: | Datatypes | Size | | ------------------------------- | ------------------------------------------------------------ | | `bool` `byte`, `ubyte`, `char` | 8-bit | | `short`, `ushort`, `wchar` | 16-bit | | `int`, `uint`, `dchar`, `float` | 32-bit | | `long`, `ulong`, `double` | 64-bit | | `real` | >= 64-bit (generally 64-bit, but 80-bit on Intel x86 32-bit) | `char` represents UTF-8 characters, `wchar`represents UTF-16 characters, and `dchar` represents UTF-32 characters. #### Type conversion For integer types, automatic type conversion is only allowed if no precision is lost (e.g. `int` to `long`). All conversion between floating point types are allowed (e.g. `double` to `float`). Manual type conversion is achieved with the `cast` expression: ```D long a = 1; int b = cast(int) a; ``` #### Type properties All types have a property `.init` to which variables of that type are initialized, if they are not initialized explicitly. For integer types, this is `0` and for floating point types it is `nan`. Every type also has a `.stringof` property which yields its name as a string. Integer types have some more properties: | Property | Description | | -------- | ----------------------------------- | | `.max` | The maximum value the type can hold | | `.min` | The minimum value the type can hold | And so do floating point types: | Property | Description | | ------------- | ----------------------------------------------------------- | | `.max` | The maximum value the type can hold | | `.min_normal` | The smallest representable normalized value that is not `0` | | `.nan` | NaN value | | `.infinity` | Infinity value | | `.dig` | number of decimal digits of precisions | | `.mant_dig` | number of bits in mantissa | | … | | #### Indexing For indexing, usually the alias type `size_t` is used, which is large enough to represent an offset into all addressable memory. ### Variable declarations Variables are declared by writing the type followed by the variable name: ```D int myVar; ``` They can also be explicitly initialized: ```D int myVar = 42; ``` It is also possible to declare several variables at once: ```D int myVar, someOtherVar; ``` D has automatic type deduction, so when explicitly initializing a variable, it is not necessary to mention the type. Instead we can use the `auto` keyword: ```D auto myVar = 42; ``` Here is a combination of the above notations: ```D auto myInt = 42, myFloat = 4.2f; ``` ### Functions The basic syntax for functions is very similar to C: ```D int add(int lhs, int rhs) { return lhs + rhs; } ``` #### Return type deduction A functions return type can be defined to be `auto`. In this case, the return type will be infered. Multiple return statements are possible, but must return compatible types. ```D auto add(int lhs, int rhs) { // returns `int` return lhs + rhs; } auto lessOrEqual(int lhs, int rhs) { // returns `double` if (lhs <= rhs) return 0; else return 1.0; } ``` #### Default arguments Those also work the same as in C and other languages: ```D void plot(string msg, string color = "red") { /* ... */ } plot("D rocks"); plot("D rocks", "blue"); ``` #### Local functions It is possible to define functions locally (even inside other functions). Those functions are not visible outside their parents scope. ```D void fun() { int local = 10; int fun_secret() { local++; // that's legal } /* … */ } static assert(!__traits(compiles, fun_secret())); // fun_secret is not visible here ``` ### Control flow #### if…else Very similar to how it is defined in other languages: ```D if (a == 5) { writeln("Condition is met"); } else if (a > 10) { writeln("Another condition is met"); } else { writeln("Nothing is met!"); } ``` #### switch…case Also very similar to how it is defined in other languages, but for it works for integer types, bools and strings (which will be covered later). ```D string myString; /* … */ switch(myString) { case "foo": writeln(`Cool, myString was "foo"`); break; default: writeln("Meh, myString was something boring"); break; } ``` For integer types, it is also possible to define ranges: ```D int c = 5; switch(c) { case 0: .. case 9: writeln(c, " is within 0-9"); break; // necessary! case 10: writeln("A Ten!"); break; default: // if nothing else matches writeln("Nothing"); break; } ``` #### Loops `while`-, `do`…`while`- and classical `for`-loops all work the same as in C++/Java etc. ##### Breaking out of outer loops As usual, you can break out of a loop immediately by using the `break` keyword. Additionally, you can also break out of outer loops by using labels: ```D outer: for (int i = 0; i < 10; ++i) { for (int j = 0; j < 5; ++j) { /* … */ break outer; // breaks out of the outer loop } } ```