The D Programming Language

klipitiklopwarrenΛογισμικό & κατασκευή λογ/κού

7 Νοε 2013 (πριν από 3 χρόνια και 9 μήνες)

186 εμφανίσεις

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 won

t)


It has
templates


but they

re easier and more flexible than in C++


it supports
compile
-
time execution


It

s
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 Python’
s


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




Default
(
.init
)

void



no value




n/a

bool


Boolean value



false

byte


signed 8 bits



0

ubyte


unsigned 8 bits


0

short


signed 16 bits



0

ushort


unsigned 16 bits


0

int



signed 32 bits



0

uint



unsigned 32 bits


0

long



signed 64 bits



0

ulong


unsigned 64 bits


0

float


32
-
bit floating
-
point

float. nan

double


64
-
bit floating
-
point

double. nan

real



largest in hardware

real. nan

char


unsigned 8
-
bit UTF
-
8

0xFF

wchar


unsigned 16
-
bit UTF
-
16

0xFFFF

dchar


unsigned 32
-
bit UTF
-
32

0x0000FFFF

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



(UTF
-
8)


wstring


wchar


(UTF
-
16)


dstring


dchar


(UTF
-
32)


foreach

visits each
character
:


foreach (c; s)




Associative Arrays


A
map

(aka

dictionary

,

hash

, etc.)


Except it’
s 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);
// Returns a ">5" delegate


writeln(g5(1));
// false


writeln(g5(6));
// 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");
// Error


writeln(g5s("bar"));


writeln(g5s("foo"));

}

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) {

// 1 4 9 16 25


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

}


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;


}

}

D

s
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 value types, operator overloading, and unit testing

import std.math;
// For abs()


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)



A thread is launched on a function basis



Using the
std.concurrency.spawn



See
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