Difference between revisions of "DIP27"

From D Wiki
Jump to: navigation, search
(Mostly spelling and grammar. Conclusion section needs clarification.)
 
(One intermediate revision by one other user not shown)
Line 26: Line 26:
  
 
== Abstract ==
 
== Abstract ==
This DIP is the first of 3. It intends to define what is a function in D. The DIP propose a radically simple approach by deciding that all function are first class function. The DIP also propose some way to mitigate the code breakage involved and allow optional parenthesis when no confusion is possible.
+
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 ==
 
== Rationale ==
  
D is a very complex languages. It promote several paradigms while being a system language. If this is a force of D, this is also a source of trouble. In fact every feature can combine itself why many other feature, some of them from different paradigms - other way of thinking - and every single special case, cause a combinatorial explosion of special cases when other features are involved.
+
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 feature together in a non trivial project. Combined together, special cases lead to surprising behaviors, unnecessary blockade, and sometime expose compiler bugs.
+
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 languages, an many corner cases have to be considered, and cause difficulties to compiler implementers and to create libraries that uses generic techniques.
+
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 specification must promote simplicity (as opposed to complexity, not as opposed to difficulty). To achieve this goal, this DIP reduce all function to a single entity : the D first class function. It get rid of function like defined in C or C++, as they are useless and cause unnecessary complexity.
+
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 definition and uses ==
Line 51: Line 51:
 
enum functionName = ReturnType function(Parameters) {
 
enum functionName = ReturnType function(Parameters) {
 
     // Function body.
 
     // Function body.
}
+
};
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Line 75: Line 75:
 
== Transitional measure to mitigate breakage ==
 
== Transitional measure to mitigate breakage ==
  
The unary & operator is defined as a NOOP when used on identifiers that resolve as function declarations. As this behavior clashes with the address of behavior, and is a special case, it must disappear after proper deprecation process is followed.
+
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.
  
 
<syntaxhighlight lang="d">
 
<syntaxhighlight lang="d">
Line 83: Line 83:
 
static assert(is(typeof(&foo) == void function()); // Transitional behavior.
 
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()); // Error (foo has no address). Final behavior.
static assert(is(typeof(&foo) == void function()*); // Pass with variant behavior expressed in possible variation section.
+
static assert(is(typeof(&foo) == void function()*); // Pass with alternate behavior expressed in the "Possible variation" section.
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Ongoing release process improvement should helps quite a lot in that regard.
+
Ongoing release process improvements should help quite a lot in that regard.
  
 
== Is Expression ==
 
== Is Expression ==
  
The function type specialization used in is expression has to be modified as well. It does match the first class function we defined above, and alias parameters as defined on [http://dlang.org/expression.html#IsExpression dlang.org website].
+
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 [http://dlang.org/expression.html#IsExpression dlang.org website].
 
<syntaxhighlight lang="d">
 
<syntaxhighlight lang="d">
 
void foo() {}
 
void foo() {}
Line 99: Line 99:
 
static assert(is(void function() == 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(typeof(foo) P == function)); // Pass, P is an empty tuple.
static assert(is(void function(uint) P == function)); // Pass, P is an a tuple of one element : uint.
+
static assert(is(void function(uint) P == function)); // Pass, P is a tuple of one element : uint.
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
== Optional parentheses ==
 
== Optional parentheses ==
  
Redundant parenthesis can be a burden for argument-less function class (or single argument function calls used as UFCS). Implicit function call is performed is the following cases :
+
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 :
 
* When .identifier lookup fails on the function :
Line 118: Line 118:
  
 
void buzz(uint a) {}
 
void buzz(uint a) {}
foo.buzz(); // Implicit call to foo is added as lookup failed on the function.
+
foo.buzz(); // An implicit call to foo is added as lookup failed on the function.
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Line 133: Line 133:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
You must note that functions are regular first class function. So optional parentheses apply regardless of how is defined the function :
+
Note that functions are regular first class functions. As such, optional parentheses apply regardless of how the function is defined :
 
<syntaxhighlight lang="d">
 
<syntaxhighlight lang="d">
 
auto foo = function uint() {
 
auto foo = function uint() {
Line 151: Line 151:
 
== Possible variation ==
 
== Possible variation ==
  
Function declaration are defined here as first class functions enums. They could have been defined as immutable first class function. They main difference is that they have an address in this case. I played with both using customized SDC and this seems unnecessary. It has the drawback that the compiler must ensure that the function pointer have a storage somewhere and no real benefit.
+
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 ==
 
== Conclusion ==
  
This DIP have the advantage to keeps most of the convenience that exists in the current situation, while reducing drastically the complexity of the situation. It has been tested with actual code (sadly, SDC don't support many D features, so it is hard to field test on big projects) among several approach.
+
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.
 
This DIP is incomplete by itself. 2 more are coming, on properties and delegates, so the topic can be completely covered.

Latest revision as of 22:54, 17 October 2016

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.