Uniform syntax for compile-time expressions in D


Proposed syntax:


ctexpr (bool) : (T [S1 S2 ... Sn] [ ':' | '==' inexpr] ) | ct_bool_value

inexpr (bool) : pattern | pattern if (ctexpr) | if(ctexpr)

pattern: type | derived data type


Description:

T type identifier

S1..Sn introduced types

ct_bool_value compile time true or false

: direct or indirect type of ... (in case of value types: is implicitly convertible)

== is exact type of ...

derived data type e.g. S[] S[N = size_t] , E[K], E[int], class, typedef, int, MyType!(int), TheirType


Passing by alias, default type, default type and value examples are not taken into consideration in above syntax description. Examples for these case are in table below.



  1. ctexpr and pattern evaluates to true or false


  1. Both pattern and ctexpr must be true, to match template function during instantiation


  1. Introduced symbols in pattern are aliased with concrete types when evaluating ctexpr


  1. Symbols can have fixed types. If reasonable when evaluating pattern symbols can have also value e.g.:

    void changeElement(T U N : U[N=uint] if(N>100))(T array, U value, int index);

    Above template function matches all static arrays bigger than 100; N - number of declared elements in s. array; N is binded to value of type uint.


  1. Parts of ctexpr can be omitted, so it looks exactly like current declarations e.g.:

    string stringize(T : bool)(bool value); // omitted symbols and ctexpr
    string stringize(T)(T value); // omitted symbols, ":" and
    inexpr


  1. Initial T can not be redefined; currently it is rather confusing that elephant transforms into monkey in following:
    string stringize(T : T[])(T value); //what should be passed to function? array or array element?

    it should be rather:
    string stringize(T U : U[])(T value); //now everything is clear


  1. Following definition with three template arguments:

    void parse(T E : E[], U : bool, V E N : E[N] if(N>100))(T array, U flag, V sarray);

    should be just syntactic sugar for:

    void parse(T E : E[] && U : bool && V E N : E[N] if (N>100))(T array, U flag, V sarray) ;

    Above is very complicated case which would be possible, but currently it is not. Simple cases still stay simple as it is now.


  1. ... is() for metaprogramming can be dropped.


  1. It is an error if defined symbols are not used in inexpr or don't have assigned types in ctexpr


  1. If inexpr is not defined T is aliased to all given symbols

  2. Template function resolution (my guess that it would be possible). When resolving template function name it might be possible to relay on ctexpr resulting value. If compile-time time expression returns true, then there is a match. In such a case it would be possible to create templ. functions:
    S toupper(isSomeString!(S))(S input)
    Although there should be probably more calculations to know which function is more specialized.


With above proposal there can be only one syntax for: static if(), template (function) definition & static assert() & alias while is() for metaprogramming can be safely dropped. This syntax is consistent & extensible so it will allow to achieve much more.




Uniform syntax by example:



Template (function) compile-time arguments:

Old

New

Comment

Template function compile-time arguments:

template normalizedType(T)

template normalizedType(T);


template normalizedType(T : U[N], U, size_t N)

template normalizedType(T U N : U[N=size_t])

Introduced variables are before their usage

void doIt(alias T E : E[])(E elem)

void doIt(alias T E : E[])(E elem)

Alias parameters; I am not sure if condition checks would work for aliases.

void doIt(T=string E : E[])(E elem)

void doIt(string T E : E[])(E elem)

I am not sure if checking condition works right now.

size_t levenshteinDistance(string equals = "a == b", Range1, Range2)(Range1 s, Range2 t)

size_t levenshteinDistance(string equals = "a == b", Range1, Range2)(Range1 s, Range2 t)

Default template parameter value will stay same as before. Please notice better consistency with situation when only type is default (row above) than in current version. And more possibilities to specify conditions.

Things currently not possible:


void do(T : class)(T instance);



void do(T E : E[class])(T sarray, E element);

match associative arrays with key as class


S toupper(isSomeString!(S))(S input)

See point 11

static if

static if (is(T : Storage!(STORAGETYPE)))

static if (T : Storage!(STORAGETYPE))

'is' probably can be safely dropped

static if (is(VALUE TYPE == typedef))

static if (VALUE TYPE == typedef)

1. This one is a bit tricky. Alternative could be:

static if (VALUE TYPE == typedef(TYPE))

to be exact pattern matching without 'magic'
2. Here I used '==' instead of ':' It should mean that we are expecting exact matches. Using ':' causes also indirect cases: derived classes and types which can be implicitly casted. In many cases it will be evaluated to exactly same result

static if (is(T TYPE == TYPE*) || is(T == class))

static if (T TYPE == TYPE* || T == class)


static if (isString!(VALUE))

static if (isString!(VALUE))

it should still work as static if takes as parameter boolean value, which is exactly what we get after evaluating isString!(T).

static if (is(VALUE TYPE == TYPE*))

static if (VALUE TYPE == TYPE*)


Things currently not possible:


static if (T : Storage!(STORAGETYPE) if (T == MyType))



static if(T == invariant)

well I am not much in const stuff, but uniform syntax opens door for this also.

static assert

static assert(is(VALUE : class), "Classes are not supported");

static assert(VALUE : class, "Classes are not supported");


Things currently not possible:


static assert(V E K : E[K] if (K == int), "Not supported");

Well, not very good example because it is enough to write:

static assert(V E : E[int], "Not supported");


Just to get idea what can be possible.

alias syntax

alias Variant[][] table;

alias Variant[][] table;














Things currently not possible:


alias Variant[][] A B C;

aliases Variant[][] to all symbols: A and B and C


alias T U : U[];

simple decomposition of types
aliases U to element type of T; if the whole compile-time expression evaluates to false it is compile-time error.
currently it is necessary to use artificial static if () statement to decompose type.


alias T U N : U[N=uint] if (N>100);

aliases U to static array element type; and N to value of type uint, but only if size of static array is bigger than 100.