440 likes | 651 Views
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.
E N D
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-safetransactions • 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 (excludesn) • 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 positionnumbers • 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)
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 PolymorphismCompile-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 FunctionsFor 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 StatementOptions: 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 ExampleAlso 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