Language Designs Explained

From D Wiki
Jump to: navigation, search

Function attributes

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 function attributes 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 an '@' 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 create a special rule in the syntax to handle non-keyword function attributes without an '@' character?

The idea would be to add a new grammar rule for function attributes like this:

<function-attribute> => <keyword> | "@" <identifier> | "safe" | "nogc" | ...

This would allow a function to have attributes that are not keywords at the expense of syntax complexity. This solves the problem of redundancy, however, it is unknown whether or not this would make the grammar of the language ambiguous. If the function attributes were restricted to appear on the right hand side of a function signature (after the parameters), then this may solve the ambiguity problem. The other concern would be that this would complicate the syntax. The compiler and every syntax highlighter would need to handle this special case which can seem like a hack. In addition, this may seem confusing to some people. If a word is used as a function attribute, they may assume it is also a keyword, and if someone were to use it as an identifier this may be confusing. This would also create inconsistency with the brace and colon syntax:

void foo1() safe;
@safe {
  void foo2();
}
@safe:
  void foo3();

These examples all apply the "safe" attribute to their respective function, but now some use an '@' and some don't.

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 inconsistency 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 wasn't nogc made a keyword?

Ideally, if the language were designed from scratch, nogc would be made a keyword. However, since the language has been around for a while, it is deemed undesirable to introduce new keywords. The reason is that before nogc was introduced, some existing code may have used identifiers called nogc; if it were suddenly regarded as a keyword now, that code would no longer compile, and the author would need to revise all parts of the code that used that identifier.

Operators

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 have a construct that is also valid C code but with subtly different semantics. The potential benefits were not worth the break in compatibility with C.