The D Programming Language

Functions that manipulate other functions.

License
Boost License 1.0.
Authors
Andrei Alexandrescu
Source:
std/functional.d

template  unaryFun(alias fun, bool byRef = false, string parmName = "a")

Transforms a string representing an expression into a unary function. The string must use symbol name a as the parameter.

Example:
alias unaryFun!("(a & 1) == 0") isEven;
assert(isEven(2) && !isEven(1));

template  binaryFun(alias fun, string parm1Name = "a", string parm2Name = "b")

Transforms a string representing an expression into a Boolean binary predicate. The string must use symbol names a and b as the compared elements.

Example:
alias binaryFun!("a < b") less;
assert(less(1, 2) && !less(2, 1));
alias binaryFun!("a > b") greater;
assert(!greater("1", "2") && greater("2", "1"));

template  binaryReverseArgs(alias pred)

Binary predicate that reverses the order of arguments, e.g., given pred(a, b), returns pred(b, a).


template  not(alias pred)

Negates predicate pred.

Example:
string a = "   Hello, world!";
assert(find!(not!isWhite)(a) == "Hello, world!");

template  curry(alias fun, alias arg)

Curries fun by tying its first argument to a particular value.

Example:
int fun(int a, int b) { return a + b; }
alias curry!(fun, 5) fun5;
assert(fun5(6) == 11);


Note that in most cases you'd use an alias instead of a value assignment. Using an alias allows you to  curry template functions without committing to a particular type of the function.

template  adjoin(F...) if (F.length)

Takes multiple functions and adjoins them together. The result is a std.typecons.Tuple with one element per passed-in function. Upon invocation, the returned tuple is the adjoined results of all functions.

Example:
static bool f1(int a) { return a != 0; }
static int f2(int a) { return a / 2; }
auto x = adjoin!(f1, f2)(5);
assert(is(typeof(x) == Tuple!(bool, int)));
assert(x[0] == true && x[1] == 2);

template  compose(fun...)

Composes passed-in functions fun[0], fun[1], ... returning a function f(x) that in turn returns fun[0](fun[1](...(x))).... Each function can be a regular functions, a delegate, or a string.

Example:
// First split a string in whitespace-separated tokens and then

// convert each token into an integer

assert(compose!(map!(to!(int)), split)("1 2 3") == [1, 2, 3]);

template  pipe(fun...)

Pipes functions in sequence. Offers the same functionality as compose, but with functions specified in reverse order. This may lead to more readable code in some situation because the order of execution is the same as lexical order.

Example:
// Read an entire text file, split the resulting string in

// whitespace-separated tokens, and then convert each token into an

// integer

int[] a = pipe!(readText, split, map!(to!(int)))("file.txt");

ReturnType!fun  memoize(alias fun, uint maxSize = (uint).max)(ParameterTypeTuple!fun args);

Memoizes a function so as to avoid repeated computation. The memoization structure is a hash table keyed by a tuple of the function's arguments. There is a speed gain if the function is repeatedly called with the same arguments and is more expensive than a hash table lookup. For more information on memoization, refer to this book chapter.

Example:
double transmogrify(int a, string b)
{
   ... expensive computation ...
}
alias memoize!transmogrify fastTransmogrify;
unittest
{
    auto slow = transmogrify(2, "hello");
    auto fast = fastTransmogrify(2, "hello");
    assert(slow == fast);
}


Technically the memoized function should be pure because  memoize assumes it will always return the same result for a given tuple of arguments. However,  memoize does not enforce that because sometimes it is useful to  memoize an impure function, too.

To memoize a recursive function, simply insert the memoized call in lieu of the plain recursive call. For example, to transform the exponential-time Fibonacci implementation into a linear-time computation:
Example:
ulong fib(ulong n)
{
    alias memoize!fib mfib;
    return n < 2 ? 1 : mfib(n - 2) + mfib(n - 1);
}
...
assert(fib(10) == 89);


To improve the speed of the factorial function,
Example:
ulong fact(ulong n)
{
    alias memoize!fact mfact;
    return n < 2 ? 1 : n * mfact(n - 1);
}
...
assert(fact(10) == 3628800);


This memoizes all values of fact up to the largest argument. To only cache the final result, move  memoize outside the function as shown below.
Example:
ulong factImpl(ulong n)
{
    return n < 2 ? 1 : n * mfact(n - 1);
}
alias memoize!factImpl fact;
...
assert(fact(10) == 3628800);


The maxSize parameter is a cutoff for the cache size. If upon a miss the length of the hash table is found to be maxSize, the table is simply cleared.
Example:
// Memoize no more than 128 values of transmogrify

alias memoize!(transmogrify, 128) fastTransmogrify;

auto  toDelegate(F)(auto ref F fp) if (isCallable!F);

Convert a callable to a delegate with the same parameter list and return type, avoiding heap allocations and use of auxiliary storage.

Examples
void doStuff() {
    writeln("Hello, world.");
}

void runDelegate(void delegate() myDelegate) {
    myDelegate();
}

auto delegateToPass = toDelegate(&doStuff);
runDelegate(delegateToPass);  // Calls doStuff, prints "Hello, world."

Known Bugs
  • Does not work with @safe functions.
  • Ignores C-style / D-style variadic arguments.