D Programming Language 1.0

Last update Mon Dec 31 10:53:28 2012


Bind function arguments to functions.


Tomasz Stachowiak

November 28, 2006


DynArg!(0) _0;
DynArg!(1) _1;
DynArg!(2) _2;
DynArg!(3) _3;
DynArg!(4) _4;
DynArg!(5) _5;
DynArg!(6) _6;
DynArg!(7) _7;
DynArg!(8) _8;
DynArg!(9) _9;
When passed to the 'bind' function, they will mark dynamic params - ones that aren't statically bound In boost, they're called _1, _2, _3, etc.. here _0, _1, _2, ...

struct Tuple(T...);
A simple tuple struct with some basic operations

template appendT(X)
Statically yields a tuple type with an extra element added at its end

appendT!(X) append(X)(X x);
Yields a tuple with an extra element added at its end

template prependT(X)
Statically yields a tuple type with an extra element added at its beginning

prependT!(X) prepend(X)(X x);
Yields a tuple with an extra element added at its beginning

template concatT(T...)
Statically concatenates this tuple type with another tuple type

struct Tuple();
An empty tuple struct

alias type;
an empty built-in tuple

alias value;
an empty built-in tuple

Tuple!(T) tuple(T...)(T t);
Dynamically create a tuple from the given items

template isTypeTuple(T)
Checks whether a given type is the Tuple struct of any length

template minNumArgs(alias fn,fnT = typeof(&fn))
Finds the minimal number of arguments a given function needs to be provided

class BoundFunc(FT,alias FAlias_,AllBoundArgs_);
A context for bound/curried functions

typeof(new BoundFunc!(FT,NullAlias,Tuple!(ArgList))) bind(FT, ArgList...)(FT fp, ArgList args);
bind() can curry or "bind" arguments of a function, producing a different function which requires less parameters, or a different order of parameters. It also allows function composition.

The syntax of a bind() call is:

bind(function or delegate pointer { , argument });

argument can be one of:
  • static/bound argument (an immediate value)
  • another bound function object
  • dynamic argument, of the form _[0-9], e.g. _0, _3 or _9

The result is a function object, which can be called using call(), func() or opCall(). There also exists a convenience function, ptr() which returns a delegate to call/func/opCall

The resulting delegate accepts exactly as many parameters as many distinct dynamic arguments were used.
- bind(&foo, _0, _1) // will yield a delegate accepting two parameters
- bind(&foo, _1, _0) // will yield a delegate accepting two parameters
- bind(&bar, _0, _1, _2, _0) // will yield a delegate accepting three parameters

The types of dynamic parameters are extracted from the bound function itself and when necessary, type negotiation is performed. For example, binding a function
void foo(int a, long b)

// with:
bind(&foo, _0, _0)
will result in a delegate accepting a single, optimal parameter type. The best type is computed using std.typetuple.DerivedToFront, so in case of an int and a long, long will be selected. Generally, bind will try to find a type that can be implicitly converted to all the other types a given dynamic parameter uses.

in case of numeric types, an explicit, but transparent (to the user) cast will be performed

Function composition works intuitively:
bind(&f1, bind(&f2, _0))

which will yield a delegate, that takes the argument, calls f2, then uses the return value of f2 to call f1. Mathematically speaking, it will yield a function composition:

When one function is composed multiple times, it will be called multiple times - Bind does no lazy evaluation, so
bind(&f3, bind(&f4, _0), bind(&f4, _0))
will produce a delegate, which, upon calling, will invoke f4 two times to evaluate the arguments for f3 and then call f3

One another feature that bind() supports is automatic tuple expansion. It means that having functions:
void foo(int a, int b)
Tuple!(int, int) bar()

Allows them to be bound by writing:
bind(&foo, bind(&bar))
// or
bind(&foo, tuple(23, 45))

template bindAlias(alias FT)
bindAlias() is similar to bind(), but it's more powerful. Use bindAlias() rather than bind() where possible.

The syntax is:

bindAlias!(Function)(argument, argument, argument, argument, ...);

bindAlias takes advantage of using aliases directly, thus being able to extract default values from functions and not forcing the user to bind them. It doesn't, however mean that the resulting delegate can be called, omitting some of its parameters. It only means that these arguments that have default values in the function provided to bindAlias don't have to be bound explicitly.

Additionally, bindAlias takes care of functions with out/inout parameters, by converting them to pointers internally. A function like:
void foo(inout a)
can be bound using:
int x;

there is no bind-time check for reference nullness, there is however a call-time check on all references which can be disabled by using version=BindNoNullCheck or compiling in release mode.