Thread overview
Uniform syntax for templates (second try)
Oct 11, 2008
Marcin Kuszczak
Oct 12, 2008
Marcin Kuszczak
Oct 12, 2008
Marcin Kuszczak
Oct 12, 2008
Marcin Kuszczak
Oct 12, 2008
Nick Sabalausky
Oct 13, 2008
Bill Baxter
Oct 12, 2008
Sergey Gromov
October 11, 2008
This is my second try to propose better syntax for templates. My previous proposal was added to bugzilla: http://d.puremagic.com/issues/show_bug.cgi?id=1827

In this proposal I modified a little bit syntax, took into consideration more cases and put into table few examples of new syntax compared to old one.

Attached html version of document. Other formats are too big to send it through news group, but they are available on request.

I hope for comments from you :-)

-- 
Regards
Marcin Kuszczak (Aarti_pl)
-------------------------------------
Ask me why I believe in Jesus - http://www.zapytajmnie.com (en/pl)
Doost (port of few Boost libraries) - http://www.dsource.org/projects/doost/
-------------------------------------



October 11, 2008
Marcin Kuszczak wrote:
> This is my second try to propose better syntax for templates. My previous
> proposal was added to bugzilla:
> http://d.puremagic.com/issues/show_bug.cgi?id=1827
> 
> In this proposal I modified a little bit syntax, took into consideration
> more cases and put into table few examples of new syntax compared to old
> one.
> 
> Attached html version of document. Other formats are too big to send it
> through news group, but they are available on request.
> 
> I hope for comments from you :-)

I'll make some candid comments without speaking for Walter.

a) The proposal prevents creating names in expressions, something we were talking about:

formattedRead("%s", &(int n));

The sequence "int n" will be ambiguous with a sequence of types.

b) At point 4 my eyes started glazing a bit.

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

The N=uint jumps as a surprising baroque element. Besides, U[N=uint] could be interpreted as a hash with uint as the default key type.

The rewrite in current D2 is:

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

To me it's not clear which is better. The second actually may be more expressive because you can collectively use all elements of the template in one place, something much clunkier in the other version.

c) I totally agree that this should be abolished:

string stringize(T : T[])(T value);

d) Let's try another example.

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

In D2:

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

e) This will be very hard to understand for both human and machine:

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

f) You mention this is not possible, but they are:

void do(T : class)(T instance);
===>
void do(T : Object)(T instance);

void do(T E : E[class])(T sarray, E element);
===>
void do(K, V)(V[K] sarray, K element) if (is(K : Object))

S toupper(isSomeString!(S))(S input)
===>
S toupper(S)(S input) if (isSomeString!(S))

static if (T : Storage!(STORAGETYPE) if (T == MyType))
===>
static if (is(MyType : Storage!(STORAGETYPE), STORAGETYPE)

static if(T == invariant)
===>
static if(T == invariant) ----- should be added

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

g) I think this is a rather minor improvement:

alias Variant[][] A B C;

h) I think this should be supported one way or another. Decomposing types quickly and easily would be a boon.

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


Andrei
October 12, 2008
Andrei Alexandrescu wrote:

> Marcin Kuszczak wrote:
>> This is my second try to propose better syntax for templates. My previous proposal was added to bugzilla: http://d.puremagic.com/issues/show_bug.cgi?id=1827
>> 
>> In this proposal I modified a little bit syntax, took into consideration more cases and put into table few examples of new syntax compared to old one.
>> 
>> Attached html version of document. Other formats are too big to send it through news group, but they are available on request.
>> 
>> I hope for comments from you :-)
> 
> I'll make some candid comments without speaking for Walter.

First of all thanks for your comments. I really appreciate them. My comments are inlined below.


> a) The proposal prevents creating names in expressions, something we were talking about:
> 
> formattedRead("%s", &(int n));
> 
> The sequence "int n" will be ambiguous with a sequence of types.

What is this proposal about? Is there some topic on NG? Is above example compile time feature? or runtime?

Well it was difficult for me to take it into consideration, because I was unaware of this proposal... :-)

> b) At point 4 my eyes started glazing a bit.
> 
> void changeElement(T U N : U[N=uint] if(N>100))
>      (T array, U value, int index);
> 
> The N=uint jumps as a surprising baroque element.

Another alternative would be e.g.:
void changeElement(T U N=uint : U[N] if(N>100)) (T array, U value, int
index);
Above was in my first proposal (in bugzilla). Then default value could be
given as N=uint(5) where necessary.

or

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

What ever syntax would be choosen, it should be also choosen for default template parameters.

Maybe some other syntax for default parameter type/value should be invented. I don't care so much about that until the main advantages (summarized at the end of this post) will be kept...

> Besides, U[N=uint] could be interpreted as a hash with uint as the default key type.

No, because you are "fixing" one dimension (type) of type variable, and are still expecting N to be defined by compiler. On compile time it is only possible for static array, where N (value) is known at compile time. For associative arrays it would be an error.

Side note: In fact it seems that arrays can be treated as special case of associative arrays with perfect hashes, so your notice is fully understandable ;-)


> The rewrite in current D2 is:
> 
> void changeElement(T : U[N], U, uint N)
>      (T array, U value, int index)
>      if (N > 100);
> 
> To me it's not clear which is better. The second actually may be more expressive because you can collectively use all elements of the template in one place, something much clunkier in the other version.

Yes, you are right. And after rethinking this case I think that current if()
after function arguments can stay as it is with global access to all
defined symbols. But in my opinion it should be added also as (optional)
extension for pattern matching expression. This way it will be possible to
use such expression in every compile time statement.

> c) I totally agree that this should be abolished:
> 
> string stringize(T : T[])(T value);

Yeah. I know :-)


> d) Let's try another example.
> 
> void parse(T E : E[], U : bool, V E N : E[N] if(N>100))
>      (T array, U flag, V sarray);
> 
> In D2:
> 
> void parse(E, U : bool, V : E[N], uint N)
>      (E[] array, U flag, V sarray)
>      if(N>100);
> 

It's not the same. In my version you restrict first template parameter to be an array, in your example it can be any type. And additionally, isn't declaration of N after its usage ugly ;-)


> e) This will be very hard to understand for both human and machine:
> 
> S toupper(isSomeString!(S))(S input)

Please don't consider this example and my comments below as part of my main proposal. This example is more about template specialization resolution, not about syntax of compile time expression. I didn't put much thinking in template specialization resolution so below only few random thoughts:

It might be possible to define score based template function resolution. For every function some logic in compiler would calculate points for "is function matching". In case one of parameters don't match score would be 0 (false). In other cases there should be chosen function with maximal score.

Well, I will stop now, as ground is getting more and more swampy :-)


> f) You mention this is not possible, but they are:
> 
> void do(T : class)(T instance);
> ===>
> void do(T : Object)(T instance);

This will not work for other categories than class and should be considered rather as workaround of problem - not solution. Try to make the same with struct, union, typedef etc. etc.

> void do(T E : E[class])(T sarray, E element);
> ===>
> void do(K, V)(V[K] sarray, K element) if (is(K : Object))

Not an equivalent. You allows here any types. In my version you allow only associative arrays with classes as key. My function will not be used for template function resolution. Comment for previous case also applies. And my syntax is shorter...

> S toupper(isSomeString!(S))(S input)
> ===>
> S toupper(S)(S input) if (isSomeString!(S))

See my comment to your "e"

> static if (T : Storage!(STORAGETYPE) if (T == MyType))
> ===>
> static if (is(MyType : Storage!(STORAGETYPE), STORAGETYPE)

You will get syntax error in your example :-). It could be avoided with less parenthesises. STORAGETYPE is not introduced parameter. Well my example is also not very good. Although it would work for following case:

alias int STORAGETYPE;

class MyType : Storage!(STORAGETYPE) {
}

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


> static if(T == invariant)
> ===>
> static if(T == invariant) ----- should be added

You will probably just get:
static if(is(T == invariant))

I reserved already version without is() :-)

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

Please notice that my syntax doesn't disallow shorter version: static assert(V E : E[int], "Not supported");

In such a case it is even shorter.

> g) I think this is a rather minor improvement:
> 
> alias Variant[][] A B C;

I agree. Probably it should be just a syntax error.


> h) I think this should be supported one way or another. Decomposing types quickly and easily would be a boon.
> 
> alias T U : U[];
> alias T U N : U[N=uint] if (N>100);

Yep. It's usefull in some cases. But why not to do it in same way as other compile time constructs and introduce something new?

---

As general comment I would like to notice that I am not considering this syntax as finished work. There might be some changes in it, and I even don't care too much about all details which should be adjusted. In my opinion major advantages of this proposal are:

1. Same syntax for practically every compile time expression in D. You have to learn it only once and then intuitively you will apply same pattern in every case. Currently you have to learn by heart the whole IsExpression table and additionally syntax for template functions. E.g. matching static arrays, is far from intuitive, and even I would say that it is against good practices of programming which demands that symbols are defined *before* usage, not after.

2. Dropping redundant IsExpression for templates. This makes a lot of code easier to read (e.g. because of less parenthesis). The notation gets also shorter.

3. My proposal is more flexible & extensible than current syntax(-es). It's also shorter in general case. And adding additional possibilities to this compile time expression will allow to use them in all compile time statements, differently than it is now.

4. It seems additionally that better pattern matching will render redundant many use cases for if(ct_expression).


-- 
Regards
Marcin Kuszczak (Aarti_pl)
-------------------------------------
Ask me why I believe in Jesus - http://www.zapytajmnie.com (en/pl)
Doost (port of few Boost libraries) - http://www.dsource.org/projects/doost/
-------------------------------------

October 12, 2008
Marcin Kuszczak wrote:
> Andrei Alexandrescu wrote:
>> formattedRead("%s", &(int n));
>>
>> The sequence "int n" will be ambiguous with a sequence of types.
> 
> What is this proposal about? Is there some topic on NG? Is above example
> compile time feature? or runtime? 

It's just that you can introduce a symbolic definition whenever an expression is expected. So "int n" is good as an expression of type int.

>> d) Let's try another example.
>>
>> void parse(T E : E[], U : bool, V E N : E[N] if(N>100))
>>      (T array, U flag, V sarray);
>>
>> In D2:
>>
>> void parse(E, U : bool, V : E[N], uint N)
>>      (E[] array, U flag, V sarray)
>>      if(N>100);
>>
> 
> It's not the same. In my version you restrict first template parameter to be
> an array, in your example it can be any type. And additionally, isn't
> declaration of N after its usage ugly ;-)

It is the same. Look at how E is used.

>> f) You mention this is not possible, but they are:
>>
>> void do(T : class)(T instance);
>> ===>
>> void do(T : Object)(T instance);
> 
> This will not work for other categories than class and should be considered
> rather as workaround of problem - not solution. Try to make the same with
> struct, union, typedef etc. etc.

I can by saying:

void do(T)(T instance) if (is(T == whatever));

>> void do(T E : E[class])(T sarray, E element);
>> ===>
>> void do(K, V)(V[K] sarray, K element) if (is(K : Object))
> 
> Not an equivalent. You allows here any types. In my version you allow only
> associative arrays with classes as key. My function will not be used for
> template function resolution. Comment for previous case also applies. And
> my syntax is shorter...

The D2 variant is the same because it restricts K to be a class. Also D2  eliminates the function from the overload set if the "if" clause is not respected (it's pretty much the whole point of the feature).

>> S toupper(isSomeString!(S))(S input)
>> ===>
>> S toupper(S)(S input) if (isSomeString!(S))
> 
> See my comment to your "e"

Equivalent.

>> static if (T : Storage!(STORAGETYPE) if (T == MyType))
>> ===>
>> static if (is(MyType : Storage!(STORAGETYPE), STORAGETYPE)
> 
> You will get syntax error in your example :-).

I know. That's a bug in the compiler.

> It could be avoided with less
> parenthesises. STORAGETYPE is not introduced parameter. Well my example is
> also not very good. Although it would work for following case:
> alias int STORAGETYPE;
> 
> class MyType : Storage!(STORAGETYPE) {
> }
> 
> static if (T : Storage!(STORAGETYPE) if (T == MyType))
> 
> 
>> static if(T == invariant)
>> ===>
>> static if(T == invariant) ----- should be added
> 
> You will probably just get:
> static if(is(T == invariant))

Correct.

> I reserved already version without is() :-)
>  
>> static assert(V E K : E[K] if (K == int), "Not supported");
>> ===>
>> static assert(is(V : E[int], E), "Not supported");
> 
> Please notice that my syntax doesn't disallow shorter version:
> static assert(V E : E[int], "Not supported");
> 
> In such a case it is even shorter.
> 
>> g) I think this is a rather minor improvement:
>>
>> alias Variant[][] A B C;
> 
> I agree. Probably it should be just a syntax error.
> 
>  
>> h) I think this should be supported one way or another. Decomposing
>> types quickly and easily would be a boon.
>>
>> alias T U : U[];
>> alias T U N : U[N=uint] if (N>100);
> 
> Yep. It's usefull in some cases. But why not to do it in same way as other
> compile time constructs and introduce something new?
> 
> ---
> 
> As general comment I would like to notice that I am not considering this
> syntax as finished work. There might be some changes in it, and I even
> don't care too much about all details which should be adjusted. In my
> opinion major advantages of this proposal are:
> 
> 1. Same syntax for practically every compile time expression in D. You have
> to learn it only once and then intuitively you will apply same pattern in
> every case. Currently you have to learn by heart the whole IsExpression
> table and additionally syntax for template functions. E.g. matching static
> arrays, is far from intuitive, and even I would say that it is against good
> practices of programming which demands that symbols are defined *before*
> usage, not after.

Uniformity is good.

> 2. Dropping redundant IsExpression for templates. This makes a lot of code
> easier to read (e.g. because of less parenthesis). The notation gets also
> shorter.

I think this is rather minor.

> 3. My proposal is more flexible & extensible than current syntax(-es). It's
> also shorter in general case. And adding additional possibilities to this
> compile time expression will allow to use them in all compile time
> statements, differently than it is now.

I'm not sure about terseness. Your patterns tend to introduce more symbols.

> 4. It seems additionally that better pattern matching will render redundant
> many use cases for if(ct_expression).

Yah, that I'm looking forward to.

This is a big change. To sell this big change to people and Walter, you need to have some smashing advantages. So far you have I'm not seeing a smashing advantage. Easily decomposing types in patterns would be one, but that is something that could be pulled out of your proposal and implemented alone.


Andrei
October 12, 2008
Andrei Alexandrescu wrote:

> Marcin Kuszczak wrote:
>> As general comment I would like to notice that I am not considering this syntax as finished work. There might be some changes in it, and I even don't care too much about all details which should be adjusted. In my opinion major advantages of this proposal are:
>> 
>> 1. Same syntax for practically every compile time expression in D. You have to learn it only once and then intuitively you will apply same pattern in every case. Currently you have to learn by heart the whole IsExpression table and additionally syntax for template functions. E.g. matching static arrays, is far from intuitive, and even I would say that it is against good practices of programming which demands that symbols are defined *before* usage, not after.
> 
> Uniformity is good.
> 
>> 2. Dropping redundant IsExpression for templates. This makes a lot of code easier to read (e.g. because of less parenthesis). The notation gets also shorter.
> 
> I think this is rather minor.

IsExpression is causing problems for newbies, as I already mentioned before.
Is syntax for templates was mentioned as difficult for students of D
language. And it must be learnt by heart. (Well, maybe that's opposite:
template function parameters must be learnt by heart, as they are
inconsistent with is()?)

>> 3. My proposal is more flexible & extensible than current syntax(-es). It's also shorter in general case. And adding additional possibilities to this compile time expression will allow to use them in all compile time statements, differently than it is now.
> 
> I'm not sure about terseness. Your patterns tend to introduce more symbols.

You will need more symbols only when you need to take more information from expression. There will be always version which will introduce not more symbols than currently.

>> 4. It seems additionally that better pattern matching will render redundant many use cases for if(ct_expression).
> 
> Yah, that I'm looking forward to.
> 
> This is a big change. To sell this big change to people and Walter, you need to have some smashing advantages. So far you have I'm not seeing a smashing advantage. Easily decomposing types in patterns would be one, but that is something that could be pulled out of your proposal and implemented alone.

Well, I am not saying that this is top-priority change right now. I am just saying that it would be better to have it implemented - it should be a way to go. I really don't have an idea how much work is it to implement this. The fact is that most pieces are already in-place, they just have to be connected together. Maybe it would be possible to introduce this change partially, finally depreciating is() completely? There will be probably not so much impact on user code, as only heavily templated code (using matching by e.g. static arrays) will be affected. Common cases will stay exactly the same.

-- 
Regards
Marcin Kuszczak (Aarti_pl)
-------------------------------------
Ask me why I believe in Jesus - http://www.zapytajmnie.com (en/pl)
Doost (port of few Boost libraries) - http://www.dsource.org/projects/doost/
-------------------------------------

October 12, 2008
Marcin Kuszczak wrote:
> Well, I am not saying that this is top-priority change right now. I am just
> saying that it would be better to have it implemented - it should be a way
> to go. I really don't have an idea how much work is it to implement this.
> The fact is that most pieces are already in-place, they just have to be
> connected together. Maybe it would be possible to introduce this change
> partially, finally depreciating is() completely? There will be probably not
> so much impact on user code, as only heavily templated code (using matching
> by e.g. static arrays) will be affected. Common cases will stay exactly the
> same.

Why are you sure deprecating is() is a goal in and of itself? You mention beginners are having trouble understanding it, but then you'd need to make a pretty good case that beginners won't have much trouble understanding your way. (It took me a while.)

Besides one good thing about is() is that it allows you to match type patterns without error. For example:

static if (is(T = V[K], K, V)) {
    // T is a hash, use K and V
} else static if (is(T = V[], V)) {
    // T is an array, use V
} ...

In contrast, aliasing can only fail with a compile-time error:

alias T K V : T = V[K];

I agree that using a symbol before clarifying that it's being introduced is odd. Anyhow, it would be great if more people added to this exchange to get a feel of the level of interest.


Andrei
October 12, 2008
Andrei Alexandrescu wrote:

> Marcin Kuszczak wrote:
>> Well, I am not saying that this is top-priority change right now. I am just saying that it would be better to have it implemented - it should be a way to go. I really don't have an idea how much work is it to implement this. The fact is that most pieces are already in-place, they just have to be connected together. Maybe it would be possible to introduce this change partially, finally depreciating is() completely? There will be probably not so much impact on user code, as only heavily templated code (using matching by e.g. static arrays) will be affected. Common cases will stay exactly the same.
> 
> Why are you sure deprecating is() is a goal in and of itself? You mention beginners are having trouble understanding it, but then you'd need to make a pretty good case that beginners won't have much trouble understanding your way. (It took me a while.)

They will have to learn my syntax anyway. Currently there is no way around,
as my syntax is based on is() expression from D1.0. But in my proposal
there is only one syntax to learn. Currently you have to learn how to write
is() expression and how to write template function parameters.

> Besides one good thing about is() is that it allows you to match type
> patterns without error.

It will be still possible as in your example below, but in simpler way:
static if (T V K :V[K])
I am not arguing about removing this case. It is usefull.

> For example:
> 
> static if (is(T = V[K], K, V)) {
>      // T is a hash, use K and V
> } else static if (is(T = V[], V)) {
>      // T is an array, use V
> } ...

LOL. And now you have been stroked by D template syntax nuances!

Above code is incorrect. And it is not only matter of '=' instead '=='. It is the matter of funky syntax, which doesn't keep any rules. In fact in 2.0 it's even worse than in 1.0 after adding TemplateParameterList to is() expression.

Above code should be written as below:

void main() {
    alias long[char[]] T;
    static if (is(T V == V[K], K)) {
        pragma(msg, V.stringof ~ " " ~ K.stringof);
    } else static if (is(T V == V[])) {
        pragma(msg, V.stringof);
    }
}

Who will guess how to write above without looking into docs? No one.

BTW. first pragma prints: "long const(char)[]". Is it by design or is it an
error?

> In contrast, aliasing can only fail with a compile-time error:
> 
> alias T K V : T = V[K];

This syntax would be usefull only in case where you can know for sure that T is associative array. In such a case you can safely decompose T into subtypes.

> I agree that using a symbol before clarifying that it's being introduced is odd. Anyhow, it would be great if more people added to this exchange to get a feel of the level of interest.

Well, I wonder why no one except you commented about this issue. That might
be the case that people didn't write so much code using isExpression and
complicated template function's template parameters. I have written
automatic serializer, which is currently probably the most advanced
solution for D. It has definable back-ends (SimpleText, JSon and
Binary(thanks to Bill Baxter, who wrote it)), versioning of classes,
importing old versions, thread safety, exception safety, discovering of
templated functions in user classes and optional saving results to string
or any other stream (btw. I would be happy to discuss with someone about my
design :-)). It is heavily templated code, with many different corner cases
which stresses type system a lot (e.g. I discovered that it's not possible
to create pointer to class in straight way). Maybe only few people wrote
such a code? I don't know because inconstancy just strikes in the eyes when
working with it...

-- 
Regards
Marcin Kuszczak (Aarti_pl)
-------------------------------------
Ask me why I believe in Jesus - http://www.zapytajmnie.com (en/pl)
Doost (port of few Boost libraries) - http://www.dsource.org/projects/doost/
-------------------------------------

October 12, 2008
"Marcin Kuszczak" <aarti_please_no@spam_interia.pl> wrote in message news:gctj4a$2k7p$1@digitalmars.com...
> Andrei Alexandrescu wrote:
>
> I agree that using a symbol before clarifying that it's being introduced
>> is odd. Anyhow, it would be great if more people added to this exchange to get a feel of the level of interest.
>
> Well, I wonder why no one except you commented about this issue.

I've been following, but feeling completely out of my league. The idea of more consistency in the syntaxes (as well as removing is()) sounds great, but I really haven't been using D templates and such enough to feel like I'd really know what I was talking about and actually have real informed opinions.


October 12, 2008
Sun, 12 Oct 2008 11:15:59 -0500,
Andrei Alexandrescu wrote:
> Anyhow, it would be great if more people added to this exchange to get a feel of the level of interest.

I admit I felt disappointed a couple of times when I wasn't able to use some is() tricks in a template type list.  If that's fixed then I'm fine with the rest of the syntax.

Unconditional type decomposition sounds useful.
October 13, 2008
On Mon, Oct 13, 2008 at 4:28 AM, Marcin Kuszczak <aarti_please_no@spam_interia.pl> wrote:
> Well, I wonder why no one except you commented about this issue.

I also have been glancing over this thread, but just haven't had the
time to really follow it.
But is and template matching syntax really is fu-bar right now, and
something to make it saner, more consistent and easier to remember
would be great.  Any time I want to use pattern matching is
expressions I basically open up the manual to the is expression page
right now, but I can't remember that bizarre syntax to save my life.

--bb