Dive into D

Dmitry Olshansky

What is D ?

C-style

System-level language

With (optional) GC

that aims to be...

 

Safer, Simpler, Better then C++

Timeline

2000

2016

2007

Walter Bright

compiler expert

Andrei Alexandescu

C++ guru

Joins

Timeline

2000

2016

2007

Walter Bright

C++ compiler expert

Andrei Alexandescu

C++ guru

Joins

- Slices

- AAs (maps)

- Unittests

- Fibers (~ coroutines)

- Closures

- Directly calls to C

- Metaprograming 

- Generative programming

- Immutability

- Purity

- Thread local by default

Show me the code!

#!/usr/bin/rdmd

import std.stdio;
 
void main()
{
    writeln("Hello, world!");
}
extern(C) int printf(const(char)* str, ...);
 
void main()
{
    printf("Hello, world!\n");
}

The C-style counterpart 

Simple, correct and even scriptable!

Safe and easy

Slices

void main(){
    // slice is dynamic array on GC heap
    int[] slice = [1, 2, 3, 4, 5];
    // slice the range of [1:3)
    int[] a = slice[1..4];
    assert(a == [2,3,4]);

    a ~= 6; // append 6
    assert(a == [2,3,4,6]);
    // no stomping!
    assert(slice == [1,2,3,4,5]);
    // add 2 arrays as vectors
    a[] += slice[1..$]; // $ is slice.length
    assert(a == [4,6,8,11]);

    int[] b = a.dup; // duplicate (=copy)
    b[0] = 10;
    assert(a[0] == 4);

    assert(*a.ptr == 4);
    int k = 1;
    int[] hacky = (&k)[0..1];
    assert(hacky[0] == 1);
    hacky ~= 2;
    assert(hacky.ptr != &k); //reallocated
    assert(hacky == [1, 2]);
}

Slices are views of arrays: pair of length + pointer

Length

Pointer

Builtin AAs

void main() {
    double[string] prices = [ 
        "Kid bike": 540.0,
        "iPhone": 3000.0,
        "Red running shoes": 9999.0
    ];
    if(auto p = "Red running shoes" in prices){
        writeln(*p); // prints 9999.0
    }
    assert("iPad" !in prices);
    prices["Kid bike"] += 160;
    assert(prices["Kid bike"] == 700);
}

AA - associative array, a key-value hash map:

   - any hashable type as key

   - any type as value

UFCS

Uniform Function Call Syntax

       Any free function can be called with method syntax if the first argument is of the compatible type

       Natural extension of foreign types

auto square(int x){
    return x*x;
}

auto half(double x){
    return x/2.0;
}

unittest { //built-in unittest
    // can import in local scope
    import std.conv;
    assert(4.square == 16);
    // to is a free (template) function from std.conv
    assert(45.to!string == "45");
    assert(31.half == 15.5);
}

Numeric literals

int number = 1000000;

//C++14 only(!)
unsigned int bitmask = 0b11010110;


unsigned int hex = 0xDEADBEAF
int number = 1_000_000;

uint bitmask = 0b1101_0110;

uint hex = 0xDEAD_BEAF;

C++

D

Was that 5 zeros or 6 zeros?

Error elimination

void clubEntrance(int age, string state)
{
    // Error: state == "sober" must be parenthesized when next to operator &
    if(age > 18 & state == "sober"){
        // ...
    }
    
}


void faultyCopy(int[] from, int[] to){
    auto len = (from.length, to.length);
    // Error: use '{ }' for an empty statement, not a ';'
    for(int i=0; i<len; i++); 
        to[i] = from[i];
}

bool checkLucky(int value, bool lucky)
{
    // Warning: switch case fallthrough - use 'goto case;' if intended
    switch(value){
        case 7:
            lucky = true;
        case 13:
            lucky = false;
        default:
    }
    return lucky;
}

Powerful

The power and perils of  code reuse!

Generic code

Find most general solution of the problem:

        Narrowest requirements

        Widest guarantees

        Should not need to regress to hand-written code

               (as good as hand-written code)

Define types that satisfy requirements

Apply the algorithm

...

Profit!

 

(Borrowed from Andrei Alexandrescu)

Generic min

import std.traits; // for CommonType

// _Static_ divide and conquer algorithm
// min(a,b,c,d) is min(min(a,b),min(c,d))
CommonType!T min(T...)(T args){
    static if(T.length > 2){
        // this is static recursion
        return min(min(args[0..$/2]), min(args[$/2..$]));
    }
    else static if(T.length == 2){
        return args[0] < args[1] ? args[0] : args[1];
    }
    else {
        return args[0];
    }
}

unittest{
    assert(min(3,5,-10) == -10);
    assert(min( "orange", "pear","apple", "banana") == "apple");
}

Moar code reuse!


void main(){
    import std.stdio, std.algorithm, std.range;

    stdin
        .byLine // get a range of lines
        .map!(x => x.idup) // copy each line
        .array // allocate as array 
        .sort!((a,b) => a > b) //sort in reverse order
        .each!writeln; // write out each line
}

Rev-sort an input text

With UFCS and the standard library functions

 D feels like a high-level functional language...

Compile-time features

int[] partialSums(int[] arr){
    int sum;
    int[] result = new int[arr.length];
    foreach(i,v; arr){
        sum += v;
        result[i] = sum;
    }
    return result;
}

//compile time constant
enum table = partialSums([1,2,3,4,5,6,7,8,9]);

static assert(table[2] == 6);

 - Can ask compiler to interpret any function at compile time

- Store the result as constant value: primitive, arrays, even structs

Mixins - generative programming

mixin("int a = 3");
mixin(generateMeSomeCode());

mixin - a static version of 'eval' in many script languages

Crude but practical alternative to AST macros

Operator overloading

struct Vector{
    double x,y;

    auto opBinary(string op)(Vector v)
    if(op == "+" || op == "-"){
        return mixin("Vector(x "~op~"v.x, y "~op~"v.y)");
    }
}

unittest{
    auto a = Vector(1,2);
    auto b = Vector(3,4);
    auto c = a+b;
    assert(c.x == 4);
    assert(c.y == 6);
}

Operator overloading is actually accessible with operator as static string argument

Useful to define lots of similar operators in bulk

Compile-time regex

unittest{
    string phone = "+31 650 903 7158";
    auto phoneReg = ctRegex!r"^\+([1-9][0-9]*) [0-9 ]*$";
    auto m = match(phone, phoneReg);
    assert(m);
    assert(m.captures[0] == "+31 650 903 7158");
    assert(m.captures[1] == "31");
}

Parse regular expression at compile-time

Generate D source code for matching it 

mixin code and package it up to follow the same API

....

Profit!

Scalable

Immutability

Unlike C++ D const and immutable are TRANSITIVE


    // string is alias to immutable(char)[]
    immutable(char)[] s = "hello";
    s[0] = 'H'; // won't compile
    // transitive immutability
    immutable(char*)** p = ...;
    p = ...;        // ok, p is not immutable
    *p = ...;       // ok, *p is not immutable
    **p = ...;      // error, **p is immutable
    ***p = ...;     // error, ***p is immutable

Transitively immutable ==> thread safe

Can share immutable state freely

Message passing

Milti-threaded file copying  with message passing


import std.algorithm, std.concurrency, std.stdio;

void main() {
   enum bufferSize = 1024 * 100;
   auto tid = spawn(&fileWriter);
   // Read loop
   foreach (immutable(ubyte)[] buffer; stdin.byChunk(bufferSize)) {
      send(tid, buffer);
   }
}

void fileWriter() {
   // Write loop
   for (;;) {
      auto buffer = receiveOnly!(immutable(ubyte)[])();
      tgt.write(buffer);
   }
}

Purity

Usually found in functional languages

A pure function has no observable side effect

Can't access global MUTABLE state

Only call other pure functions

// mutation inside is not observable
// from the outside
pure long factorial(int x){
    long ret = 1;
    for(int i=2; i<x; i++){
        ret *= i;
    }
    return ret;
}

Weak pure

More pragmatic version - no global state 

// weak pure - side effects limited by args
pure int[] partialSum(int[] arr){
    int sum;
    int[] result = new int[arr.length];
    foreach(i,v; arr){
        sum += v;
        result[i] = sum;
    }
    return result;
}

// can use inside of strong pure functions
pure int longestZeroSum(const(int)[] arr){
    auto partials = partialSum(arr);
    int longest = 0;
    foreach(i; 0..arr.length)
    foreach(j; i..arr.length){
        if(partials[i] == partials[j])
            longest = max(longest, j - i);
    }
    return longest;
}

Links

https://dlang.org

http://gdcproject.org

http://wiki.dlang.org/LDC

https://github.com/D-Programming-Language