Task

Demonstrate the different syntax and semantics provided for calling a function.



This may include:

   Calling a function that requires no arguments
   Calling a function with a fixed number of arguments
   Calling a function with optional arguments
   Calling a function with a variable number of arguments
   Calling a function with named arguments
   Using a function in statement context
   Using a function in first-class context within an expression
   Obtaining the return value of a function
   Distinguishing built-in functions and user-defined functions
   Distinguishing subroutines and functions
   Stating whether arguments are passed by value or by reference
   Is partial application possible and how


This task is not about defining functions.




import std.traits;
 
enum isSubroutine(alias F) = is(ReturnType!F == void);
 
void main() {
    void foo1() {}
 
    // Calling a function that requires no arguments:
    foo1();
    foo1; // Alternative syntax.
 
 
    void foo2(int x, int y) {}
 
    immutable lambda = function int(int x) => x ^^ 2;
 
    // Calling a function with a fixed number of arguments:
    foo2(1, 2);
    foo2(1, 2);
    cast(void)lambda(1);
 
 
    void foo3(int x, int y=2) {}
 
    // Calling a function with optional arguments:
    foo3(1);
    foo3(1, 3);
 
    int sum(int[] arr...) {
        int tot = 0;
        foreach (immutable x; arr)
            tot += x;
        return tot;
    }
 
    real sum2(Args...)(Args arr) {
        typeof(return) tot = 0;
        foreach (immutable x; arr)
            tot += x;
        return tot;
    }
 
    // Calling a function with a variable number of arguments:
    assert(sum(1, 2, 3) == 6);
    assert(sum(1, 2, 3, 4) == 10);
    assert(sum2(1, 2.5, 3.5) == 7);
 
    // Calling a function with named arguments:
    // Various struct or tuple-based tricks can be used for this,
    // but currently D doesn't have named arguments.
 
 
    // Using a function in statement context (?):
    if (1)
        foo1;
 
    // Using a function in first-class context within an expression:
    assert(sum(1) == 1);
 
 
    auto foo4() { return 1; }
 
    // Obtaining the return value of a function:
    immutable x = foo4;
 
 
    // Distinguishing built-in functions and user-defined functions:
    // There are no built-in functions, beside the operators, and
    // pseudo-functions like assert().
 
 
    int myFynction(int x) { return x; }
    void mySubroutine(int x) {}
 
    // Distinguishing subroutines and functions:
    // (A subroutine is merely a function that has no explicit
    // return statement and will return void).
    pragma(msg, isSubroutine!mySubroutine); // Prints: true
    pragma(msg, isSubroutine!myFynction);   // Prints: false
 
 
    void foo5(int a, in int b, ref int c, out int d, lazy int e, scope int f) {}
 
    // Stating whether arguments are passed by value, by reference, etc:
    alias STC = ParameterStorageClass;
    alias psct = ParameterStorageClassTuple!foo5;
    static assert(psct.length == 6); // Six parameters.
    static assert(psct[0] == STC.none);
    static assert(psct[1] == STC.none);
    static assert(psct[2] == STC.ref_);
    static assert(psct[3] == STC.out_);
    static assert(psct[4] == STC.lazy_);
    static assert(psct[5] == STC.scope_);
    // There are also inout and auto ref.
 
 
    int foo6(int a, int b) { return a + b; }
 
    // Is partial application possible and how:
    import std.functional;
    alias foo6b = partial!(foo6, 5);
    assert(foo6b(6) == 11);
}