Closures
Nested and higher-level functions
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++)
Higher-Level Functions and Closures
// gtn.d
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; infers type
writeln(g5(1)); // false
writeln(g5(6)); // true
}
Lambda Expressions
// gtn2.d: Anonymous function with the delegate keyword auto gtn(int n) {
return delegate bool(int m) {return m > n;};
}
// gtn3.d: The delegate keyword isn’t really needed auto gtn(int n) {
return (int m) {return m > n;};
}
Environments Are Objects
void main() { return 42; } }
class A { int fun() {
A a = new A;
auto dg = &a.fun; // A “bound method”
writeln(dg()); // 42
}
There is no
“Objects are a poor man’s closures”
vs.
“Closures are a poor man’s objects”
debate.
They are unified in D.
Parametric Polymorphism
// gtn4.d
import std.stdio;
auto gtn(T)(T n) {
return (T m) {return m > n;};
}
void main() { gtn(5);
auto g5 =
writeln(g5(1)); // false
writeln(g5(6)); // true
auto g5s = gtn(« baz »);
writeln(g5s(« bar »)); // false
writeln(g5s(« foo »)); // true
}
Compile-Time Constraints
// gtn5.d
import std.stdio, std.traits;
auto gtn(T)(T n) if (isNumeric!T) {
return (T m) {return m > n;};
}
void main() { gtn!int(5);
auto g5 =
writeln(g5(1));
writeln(g5(6));
auto g5s = gtn!string(« baz »); // Error
writeln(g5s(« bar »));
writeln(g5s(« foo »));
}
Referential Transparency via Pure Functions
// fib.d: Mutable locals are okay import std.stdio, std.conv;
pure ulong fib(uint n) {
if (n == 0 || n == 1) return n;
ulong a = 1, b = 1;
foreach (i; 2..n) { // .. is exclusive of n
auto t = b;
b += a;
a = t;
}
return b;
}
void main(string[] args) {
if (args.length > 1)
writeln(fib(to!(uint)(args[1])));
}
Program Correctness and Software Engineering
Resource Management with the scope statement
scope(exit | success | failure)
No need for try-catch-finally
Contract Programming:
Pre-conditions (enforced contravariance)
Post-conditions (enforced covariance)
Class Invariants
Software Engineering Support
-unittest, -debug, -release, -version, -profile compiler options
The scope Statement
void g() {
risky_op1();
scope(failure) undo_risky_op1(); risky_op2();
scope(failure) undo_risky_op2();
risky_op3();
writeln(« g succeeded »);
}
Preconditions, Postconditions and Class Invariants
// rational.d: Shows class-based contract programming struct Rational {
private int num = 0; private int den = 1;
// Class invariant invariant() {
assert(den > 0 && gcd(num, den) == 1);
}
…
Preconditions, Postconditions and Class Invariants
Continued
// Constructor
this(int n, int d = 1)
// Constructor precondition in {
assert(d != 0);
}
body { // Establishes class invariant num = n
den = d;
auto div = gcd(num, den); if (den < 0)
div = -div; num /= div; den /= div;
}
Rational opBinary(string op)(Rational r) if (op == « + ») { return Rational(num*r.den + den*r.num, den*r.den);
}
} // End of struct Rational
What is D?
Why Might You Care?
An Invitation to D (Relevant Examples)