You are on page 1of 41

The D Programming

Language
CS 4450

What is D?
A

system programming language


conceived in 1996 as a C++
replacement
Combines the power of C++ with
the usability of languages like
Java, C# and Python
If something works in C++, C# or
Java, it probably works pretty
much the same in D

Often, programming teams will resort to a hybrid


approach, where they will mix Python and C++,
trying to get the productivity of Python and the
performance of C++. The frequency of this
approach indicates that there is a large unmet
need in the programming language department.
D intends to fill that need. It combines the ability
to do low-level manipulation of the machine with
the latest technologies in building reliable,
maintainable, portable, high-level code. D has
moved well ahead of any other language in its
abilities to support and integrate multiple
paradigms like imperative, OOP, and generic
programming.
Walter Bright, Co-designer of D

D is like C++
It

supports static local variables


It supports pointers
but you almost never need them (we wont)
It

has templates

but theyre easier and more flexible than in


C++
it supports compile-time execution
Its

fast

compiles to native code


It

supports operator overloading

D is Like Java and C#


It

supports garbage collection

the default, but you can opt-out


It

supports explicit, user-defined


interfaces
uses : for inheritance, like C#

It

has inner classes, like Java and C#


It has delegates
but more powerful than in C#
It

distinguishes reference and value


types
It supports properties

D is Like Python and FP


Languages
It

has modules and packages

like Pythons
It

supports nested functions and


closures
including anonymous functions

It

has a pure FP subset

and the usual higher-order functions


reduce, map, filter, compose

supports lazy evaluation of function


parameters

D is for Real Programmers


It

has software engineering support:

unit testing
contract programming
invariants, preconditions, postconditions

built-in constructs for exception-safe


transactions
Supports

concurrent programming

with thread-local storage


both lock and lockless programming
Can

interoperate with C and C++

D'iving In
// hello.d
import std.stdio;
void main(string[] args) {
if (args.length > 1)
foreach (a; args[1..$])
writeln("Hello " ~ a);
else
writeln("Hello, Modern World");
}
Chuck-Allisons-iMac-4:CCSC chuck$ dmd hello.d
Chuck-Allisons-iMac-4:CCSC chuck$ ./hello
Hello, Modern World
Chuck-Allisons-iMac-4:CCSC chuck$ ./hello John Jane
Hello John
Hello Jane

Primitive Data Types


Type
Meaning
void
no value
bool
Boolean value
byte
signed 8 bits
ubyte
unsigned 8 bits
short
signed 16 bits
ushort
unsigned 16 bits
int
signed 32 bits
uint
unsigned 32 bits
long
signed 64 bits
ulong
unsigned 64 bits
float
32-bit floating-point
double
64-bit floating-point
real
largest in hardware
char
unsigned 8-bit UTF-8
wchar
unsigned 16-bit UTF-16
dchar
unsigned 32-bit UTF-32
0x0000FFFF

Default (.init)
n/a
false
0
0
0
0
0
0
0
0
float. nan
double. nan
real. nan
0xFF
0xFFFF

Literals and Type


Inference
Uses

the same suffixes as C/C++

f, l, u, etc.
Uses

the same prefixes

0123, 0x1CF
Adds binary: 0b01001
Type

inference with auto:

auto targetSalary = 15_000_000;


auto sq = (double x) { return x*x; };

Arrays
Static

arrays

Similar to arrays in C/C++


int a[3];
int[3] a; // Same thing
Dynamic

Arrays

Similar to vectors, ArrayLists, etc.


See

staticarray.d
Initialization:
int[3] a = [1:2, 3];

// [0,2,3]

Selected Array Operations


Indexing with [ ]
Concatenation with ~
Copying with a.dup
Assignment with =
Length with .length

$ = .length in index expressions (a[2..$])


m..n is an array slice (excludes n)
can assign to .length to resize dynamic
arrays

Array-wise

operations (mathematical
vector operations)
See mergesort.d, vector.d

Strings
Strings

are dynamic arrays of


immutable characters
alias immutable(char)[] string;

string char
wstring wchar
dstring dchar
foreach

(UTF-8)
(UTF-16)
(UTF-32)

visits each character:

foreach (c; s)

Associative Arrays
A

map (aka dictionary, hash, etc.)

Except its a built-in type (like dict in Python)


Uses

array syntax:

void main() {
int[string] mymap = ["one":1, "two":2];
mymap["three"] = 3;
foreach (k,v; mymap) writef("%s:%s ",k,v);
}

/* Output:
three:3 one:1 two:2
*/

Word Count Program


Output
/* Abbreviated output from the Gettysburg Address:
But,: 1
Four: 1

who: 3
will: 1
work: 1
world: 1
years: 1
*/

A Word Count Program


import std.stdio, std.array, std.file;
void wc(string filename) {
string[] words =
split(cast(string) read(filename));
int[string] counts;
foreach (word; words)
++counts[word];
foreach (w; counts.keys.sort)
writefln("%s: %d", w, counts[w]);
}
void main(string[] args) {
if (args.length == 2)
wc(args[1]);
}

Tuples
Defined

in std.typecons

Tuple!(type1,type2,..)

Helper

function:

tuple(val1,val2,)
Can use 0-based position

numbers
Can also use field names
See tuple.d

Data Qualifiers
For

function parameters:

in | out | inout
ref
lazy
General

declaration qualifiers:

const
immutable

Lazy Parameters
import std.stdio;
void f(bool b, lazy void g) {
if (b) {
g();
}
}
void main() {
f(false, writeln("executing g"));
f(true, writeln("executing g"));
}

/* Output:
executing g
*/

Closures
Nested

functions are returned as


(dynamic) closures
aka delegates (a code-environment pair)
The referencing environment could be a
function, class, or object
Escaped activation records are moved
from the stack to the garbage-collected
heap

Plain

function pointers also supported:

int function(int) f; (vs. int (*f)(int); in C)

20

Plain Function Pointers


int f1(int n) {return n+1;}
int f2(int n) {return n+2;}
int f3(int n) {return n+3;}
void main() {
auto funs = [&f1, &f2, &f3];
foreach (f; funs)
writeln(f(1));
}

/* Output:
2
3
4
*/

Higher-Level Functions and


Closures
import std.stdio;
bool delegate(int) gtn(int n) {
bool execute(int m) {
return m > n;
}
return &execute;
}
void main() {
auto g5 = gtn(5);
writeln(g5(1));
writeln(g5(6));
}

// Returns a ">5" delegate


// false
// true

Lambda Expressions
auto gtn(int n) {
return delegate bool(int m) {return m > n;};
}

// Preferred:
auto gtn(int n) {
return (int m) {return m > n;};
}

An Object Calling
Environment
void main() {
class A { int fun() { return 42; } }
A a = new A;
auto dg = &a.fun; // A bound method
writeln(dg());
// 42
}

Parametric Polymorphism
Compile-time Parameters

auto gtn(T)(T n) {
return (T m) {return m > n;};
}
void main() {
auto g5 = gtn(5);
writeln(g5(1));
writeln(g5(6));
auto g5s = gtn("baz");
writeln(g5s("bar"));
writeln(g5s("foo"));
}

Compile-Time Constraints
import std.stdio, std.traits;
auto gtn(T)(T n) if (isNumeric!T) {
return (T m) {return m > n;};
}
void main() {
auto g5 = gtn!int(5);
writeln(g5(1));
writeln(g5(6));
auto g5s = gtn!string("baz");
writeln(g5s("bar"));
writeln(g5s("foo"));
}

// Error

Compile-Time Function
Evaluation
// ctfe.d: Compile-time function execution
import std.stdio, std.conv;

int square(int i) { return i * i; }

void main() {
static int n = square(3); // compile time
writefln(text(n));
writefln(text(square(4)));
// runtime
}

Pure Functions

For Referential Transparency


import std.stdio;
import std.conv;
pure ulong fib(uint n) {
if (n == 0 || n == 1) return n;
ulong a = 1, b = 1;
foreach (i; 2..n) {
auto t = b;
b += a;
a = t;
}
return b;
}
void main(string[] args) {
if (args.length > 1)
writeln(fib(to!(uint)(args[1])));
}

Selected Higher-Level
Functions
int[] nums = [1,2,3,4,5];

auto squarelist(int[] list) {


return map!("a*a")(list);
}

// 1 4 9 16 25

auto sqsum2(int[] list) {


// 55
return reduce!("a + b*b")(0,list); // (list)
}
auto evens(int[] list) {
// 2 4
return filter!("a % 2 == 0")(list);
}
alias compose!("a/3.0","a*a","a+1.0") comp;
writeln(comp(2.0)); // 3 = (2+1)^2 / 3
alias pipe!(div3,sq,pls1) pip; // fns in scope
writeln(pip(2.0)); // 1.4444 = (2/3)^2+1

std.functional.memoize
Memoization

is a technique
where a cache of input-output
pairs is kept for function calls

Avoids

recomputing function

values
See

fib.d

Variable-length Argument
Lists

Use in the parameter definition


Homogeneous
use in the runtime parameter list
passes a dynamic array
Heterogeneous

use in the compile-time parameter


list
passes a tuple
See

varargs.d

An Unsafe Function
void g() {
risky_op1(); // May acquire resources
risky_op2();
risky_op3();
writeln("g succeeded");
}

Assume further that these functions must run to


completion or not at all.

An Unsavory Solution
void g() {
risky_op1();
try {
risky_op2();
}
catch (Exception x) {
undo_risky_op1(); // Back-out op1
throw x;
// Rethrow exception
}
try {
risky_op3();
writeln("g succeeded");
}
catch (Exception x) {
// Back-out op1 and op2 in reverse order
undo_risky_op2();
undo_risky_op1();
throw x;
}
}

Ds scope Statement
Options: exit | success | failure

void g() {
risky_op1();
scope(failure) undo_risky_op1();
risky_op2();
scope(failure) undo_risky_op2();
risky_op3();
writeln("g succeeded");
}

Contract Programming
Example

Also illustrates
operator
import
std.math;value
//types,
For abs()
and unit testing

overloading,

struct Rational {
private int num = 0;
private int den = 1;

// Local helper function


static int gcd(int m, int n) {
return n == 0 ? abs(m)
: gcd(abs(n), abs(m)%abs(n));
}
// Class invariant
invariant() {
assert(den > 0 && gcd(num, den) == 1);
}

Contract Programming
Example
(continued)
// Constructor

this(int n, int d = 1)
// Constructor precondition
in {
assert(d != 0);
}
body {
num = n;
den = d;
auto div = gcd(num, den);
if (den < 0)
div = -div;
num /= div;
den /= div;
}

Contract Programming
Example
(concluded)
// ("if" form evaluated at compile time)

Rational opBinary(string op)(Rational r)


if (op == "+)
{
return Rational(num*r.den +
den*r.num, den*r.den);
}
}
unittest {
// Compile with unittest option
auto r1 = Rational(1,2), r2 = Rational(3,4),
r3 = r1 + r2;
assert(r3.num == 5 && r3.den == 4);
}
void main(){}

Variant Types
Sometimes

you want to hold any type

hopefully not too often!


Variant

can hold any type


You can only get out what was last
put in
see variant.d
Bounded,

discriminated unions with


Algebraic
like ML unions
see variant2.d

Threads
A

thread is a path of execution


inside a running program (process)

thread is launched on a function


basis

Using
See

the std.concurrency.spawn

concur1.d

Message Passing
A paradigm for thread-to-thread
communication
Threads send/receive data to/from
each other
Less problematic (and less flexible)
than sharing data via mutexes
See concur1,2,3.d

Infinite Streams in D
Threads

and Message Passing


naturally support streams

See

stream.d

You might also like