www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Uniform syntax for templates (second try)

reply Marcin Kuszczak <aarti_please_no spam_interia.pl> writes:
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/
-------------------------------------
Oct 11 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Oct 11 2008
parent reply Marcin Kuszczak <aarti_please_no spam_interia.pl> writes:
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/ -------------------------------------
Oct 12 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Oct 12 2008
parent reply Marcin Kuszczak <aarti_please_no spam_interia.pl> writes:
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/ -------------------------------------
Oct 12 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Oct 12 2008
next sibling parent reply Marcin Kuszczak <aarti_please_no spam_interia.pl> writes:
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/ -------------------------------------
Oct 12 2008
next sibling parent "Nick Sabalausky" <a a.a> writes:
"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.
Oct 12 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
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
Oct 12 2008
prev sibling parent Sergey Gromov <snake.scaly gmail.com> writes:
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.
Oct 12 2008