DIP27

From D Wiki
Revision as of 22:54, 17 October 2016 by Mleise (talk | contribs) (Mostly spelling and grammar. Conclusion section needs clarification.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Title: Functions
DIP: 27
Version: 2
Status: Draft
Created: 2013-02-26
Last Modified: 2013-02-27
Author: Amaury SÉCHET
Links: DIP23

Abstract

This DIP is the first of 3. It intends to define what is a function in D. The DIP proposes a radically simple approach by deciding that all functions are first class functions. The DIP also proposes some way to mitigate the code breakage involved and allows optional parenthesis when no confusion is possible.

Rationale

D is a very complex language. It promotes several paradigms while being a system language. If this is a power of D, this is also a source of trouble. In fact, every feature can be combined why many other features, some of them from different paradigms - other ways of thinking - and every single special case causes a combinatorial explosion of special cases when other features are involved.

This problem is experienced by many D users that try to use advanced features together in a non-trivial project. Combined together, special cases lead to surprising behaviors, unnecessary blockade, and sometimes expose compiler bugs.

If it is impairing users, it is also impairing polishing of the language, and many corner cases have to be considered and cause difficulties to compiler implementers and to the creation of libraries that use generic techniques.

To solve, or at least reduce that problem, D's specification must promote simplicity (as opposed to complexity, not as opposed to difficulty). To achieve this goal, this DIP reduces all functions to a single entity : the D first class function. It gets rid of functions as defined in C or C++, as they are useless and cause unnecessary complexity.

Function definition and uses

Function are still defined with the same syntax :

ReturnType functionName(Parameters) {
    // Function body.
}

However, this is now strictly equivalent to :

enum functionName = ReturnType function(Parameters) {
    // Function body.
};

The function has a function type and can be used as this anywhere a function is expected :

static assert(is(typeof(functionName) == ReturnType function(Parameters)); // Pass

ReturnType function(Parameters) foo = functionName; // OK

void buzz(ReturnType function(Parameters) qux);
buzz(foo); // OK
buzz(bar); // OK

auto a = functionName;
static assert(is(typeof(a) == ReturnType function(Parameters)); // Pass

auto b = functionName();
static assert(is(typeof(b) == ReturnType); // Pass

Note that what is above is simply consequences of the simple rule expressed above.

Transitional measure to mitigate breakage

The unary &-operator is defined as a NOOP when used on identifiers that resolve to function declarations. As this behavior clashes with the address-of behavior and is a special case, it must disappear after a proper deprecation process has been followed.

void foo() {}

static assert(is(typeof(foo) == void function()); // Pass
static assert(is(typeof(&foo) == void function()); // Transitional behavior.
static assert(is(typeof(&foo) == void function()); // Error (foo has no address). Final behavior.
static assert(is(typeof(&foo) == void function()*); // Pass with alternate behavior expressed in the "Possible variation" section.

Ongoing release process improvements should help quite a lot in that regard.

Is Expression

The function type specialization used in the is-expression has to be modified as well. It must match the first class function we defined above, and alias parameters as defined on the dlang.org website.

void foo() {}
auto bar = foo;

static assert(is(typeof(foo) == function)); // Pass
static assert(is(typeof(bar) == function)); // Pass
static assert(is(void function() == function)); // Pass
static assert(is(typeof(foo) P == function)); // Pass, P is an empty tuple.
static assert(is(void function(uint) P == function)); // Pass, P is a tuple of one element : uint.

Optional parentheses

Redundant parentheses can be a burden for the argument-less function class (or single argument function calls used in UFCS). An implicit function call is performed in the following cases :

  • When .identifier lookup fails on the function :
uint foo() {
    return 0;
}

void bar(uint function() a) { writeln("function bar"); }
void bar(uint a) { writeln("uint bar"); }

foo.bar(); // function bar

void buzz(uint a) {}
foo.buzz(); // An implicit call to foo is added as lookup failed on the function.
  • When used in a foreach :
import std.algorithm;

void main() {
    foreach(i; iota(5).map!(n => n * n)) {
        import std.stdio;
        writeln(i); // Prints 1 then 4, 9, 16 and 25.
    }
}

Note that functions are regular first class functions. As such, optional parentheses apply regardless of how the function is defined :

auto foo = function uint() {
    writeln("foo called !");
    return 42;
}

void bar(uint function() a) { writeln("function bar"); }
void bar(uint a) { writeln("uint bar"); }

foo.bar(); // function bar

void buzz(uint a) {}
foo.buzz(); // foo called !

Possible variation

Function declarations are defined here as first class function enums. They could have been defined as immutable first class functions. They main difference is that they have an address in this case. I played with both using customized versions of SDC and this seems unnecessary. It has the drawback that the compiler must ensure that the function pointers have a storage somewhere and no real benefit.

Conclusion

This DIP has the advantage of keeping most of the convenience that exists in the current situation, while drastically reducing the complexity of the situation [requires clarification: language/compiler implementation?]. It has been tested on actual code that employs several of D's paradigms. (Sadly, SDC lacks support for some D features, so it is hard to field test on big projects).

This DIP is incomplete by itself. 2 more are coming, on properties and delegates, so the topic can be completely covered.

Copyright

This document has been placed in the Public Domain.