C-style
System-level language
With (optional) GC
that aims to be...
Safer, Simpler, Better then C++
2000
2016
2007
Walter Bright
compiler expert
Andrei Alexandescu
C++ guru
Joins
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
#!/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!
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
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
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);
}
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?
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;
}
The power and perils of code reuse!
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)
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");
}
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...
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
mixin("int a = 3");
mixin(generateMeSomeCode());
mixin - a static version of 'eval' in many script languages
Crude but practical alternative to AST macros
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
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!
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
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);
}
}
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;
}
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;
}
https://dlang.org
http://gdcproject.org
http://wiki.dlang.org/LDC
https://github.com/D-Programming-Language