Language Designs Explained

From D Wiki
Revision as of 19:44, 29 January 2015 by Meta (talk | contribs) (Added section on why D does not have chaining comparison operators)
Jump to: navigation, search


Why do some function attributes have an '@' and some don't?

A function attribute requires an '@' if and only if it is not a keyword. Since "pure" and "nothrow" are keywords, but "safe" and "nogc" are not, using them all together would look like this:

void foo() pure nothrow @safe @nogc;

A list of the current keywords are found here. This looks odd, however, we haven't come up with an idea we like to solve this. Read on to understand the potential solutions and the problems they create.

Why are some function attributes keywords and some not?

Originally, all functions attribute were also keywords, like pure and nothrow. Eventually, new features were added to the language that required new function attributes. However, making the new attributes into keywords would have fundamentally changed the syntax of the language and would result in breaking existing code. So instead of making these new attributes keywords, a decision was made to allow function attributes to be used that were not keywords so long as an '@' character was used. This allowed new function attributes to be added without breaking existing code. The disadvantage of this was that now a function with both kinds of attributes looks a bit odd, i.e.

void foo() @safe pure @nogc nothrow;

Because of this, in the future some of these attributes may be added as keywords. However, doing so would result in breaking any code that previously used these words somewhere else such as a variable or function name. i.e.

int safe = 0; // This code would break if "safe" was added as a keyword

Why do non-keyword attributes like safe and nogc require a '@' character?

Supporting non-keyword attributes without requiring an '@' character would remove redundancy in the language. Redundancy in a language helps

* compilers give better error messages
* syntax highlighters when the code is under development or contains errors

The less redundancy a language has the more chances that one character change can propagate an error through the entire program. Consider the following example:

void func()
safe T = 7;

This is an error since the function definition is not finished. It should have ended with a semi-colon or a function body. If D allowed non-keyword function attributes, then "safe" and "T" would be considered attributes of the function by the parser and the error would not be found until the '=' character.

Error: expected ';' or '{' after function declaration, not '='

Why don't we just allow an '@' character on all function attributes so it looks consistent?

To appreciate the answer for this remember that the original problem is a matter of consistency. It's inconsistent that some function attributes use an '@' character and some don't. If D supported putting an '@' character on the function attributes that are also keywords, then this moves the consistency somewhere else. Since keywords are re-used all over the place, some instances of the keyword will require an '@' and some will not, i.e.

@abstract void foo();
abstract class C { }

See the inconsistency? Then you might say, let's make some attributes require an '@' and some not, but how does that solve the original problem? You've just moved some keywords into the '@' bin and some into the "no @' bin. Even worse, you've now made the rule more complex. The current rule is "Use an '@' symbol if the word is not a keyword". Now the rule is "Use an '@' symbol if the word not a keyword, except when you are using the keyword as ...". When the alternatives are considered, the simple rule is not that bad of an option.

Why hasn't nogc been made a keyword?

answer under construction

Why does D not support chaining comparison operators?

First off, what are chaining comparison operators? The term refers to a certain way that comparison operators behave in various languages, which allows for writing multiple comparisons without using logical **and** between them. Chaining comparison operators are popular in more dynamic languages like Python or Perl, and also provide a slight performance benefit as the middle value only needs to be evaluated once.

Example:

age = 25

if 18 < age <= 25:
    print('Chained comparison!')

The equivalent C code would be:

int age = 25;

if (18 < age && age <= 25)
{
    printf("No chaining here!");
}

So why does D not support this behaviour for comparison operators if it is more efficient, more concise, and more elegant than the equivalent C code? It is because D strives to be backwards-compatible with C wherever possible, and comparison operator chaining is not backwards-compatible with C.

While code like 18 < age <= 25 is valid C and will generally do what you want (at least in an if-statement), it is not the same as the Python code above. First 18 < age is evaluated, producing 1 (C does not have the boolean type), which is then compared to 25, which just happens to be greater than 1. This is more evident when checking if two variables are equal to a third value:

int age1 = 25;
int age2 = 25;

if (age1 == age2 == 25)
{
    printf("Something's fishy...");
}

The above code will **NOT** print "Something's fishy...", as the expression age1 == age2 produces 1, which is then compared to 25. Since 1 is not equal to 25 the whole expression evaluates to false and the if-statement will not execute.

As was mentioned earlier, D strives to be backwards-compatible with C wherever possible. It was decided that while chaining comparison operators are somewhat desirable, it would be a bad idea to silently change the semantics of code that will also compile in C, and it is therefore not worth the breakage.