Thread overview
[Issue 1827] New: Uniform syntax for is(), static if, alias, template definition & static assert
Feb 11, 2008
d-bugmail
Feb 12, 2008
d-bugmail
Feb 12, 2008
d-bugmail
Feb 12, 2008
d-bugmail
Feb 12, 2008
d-bugmail
Feb 13, 2008
Bill Baxter
Feb 13, 2008
Janice Caron
Apr 08, 2011
Trass3r
Jan 05, 2012
Trass3r
February 11, 2008
http://d.puremagic.com/issues/show_bug.cgi?id=1827

           Summary: Uniform syntax for is(), static if, alias, template
                    definition & static assert
           Product: D
           Version: unspecified
          Platform: PC
        OS/Version: All
            Status: NEW
          Severity: enhancement
          Priority: P2
         Component: DMD
        AssignedTo: bugzilla@digitalmars.com
        ReportedBy: aarti@interia.pl


I decided to put my proposal from news list (posted some time ago) as I think it can really improve current situation with templates. After implementing quite a big piece of template code I can only say that currently the whole system is quite inconsistent, but situation can be easily improved, keeping a lot of backward compatibility.

I did not get any replays for my proposals and it's difficult for me to say
why:
- proposal is so bad?
- proposal is so good, so there is no need to comment on it?
- no one is interested?

Well - I just don't know, so I am trying once again here, in hope of getting some technical arguments... :-)

---
I wonder if you ever try to define something like this:

void parse(T : T == char[] || T == wchar[] || T == dchar[])(T str) {
}

or match associative array in template declaration?

Or maybe you got annoyed with different syntaxes for template definitions
and is() expression?

static if (is(T : U[N], U, size_t N)) ...; //ok
void parse(T : U[N], U, size_t N) ...; //doesn't work
void parse(T U: U*)(T val) {}; //doesn't work

or wonder why symbol T gets redefined into array element like below:
void parse(T : T[]) {}

or wants to define template like below:
void parse(T : isString!(T)) {}

If yes, please read on.

I will propose few simple modification for current syntax. They are not completely new, but rather based on current design with some (quite obvious) extensions and improvements. I don't know what is Walter & Andrei cooking for us in future, so maybe solution is already prepared (as according to docs from conference pattern matching should work also for macros). But nevertheless I think it will be good to share with you these ideas. If it would be implemented it is also argument against enum as manifest constant identifier in favour of alias.

-----------------

Proposed syntax:

ctexpr:   T [S1[=type] S2[=type] ... Sn[=type]] [: inexpr];
inexpr:   pattern | pattern ctexpr | ctexpr
pattern: type | derived data type; // previously defined symbols
                                   // S can be used here

Derived data type is e.g. array E[], associative array E[K], pointer P* etc.

1. ctexpr and pattern (when there is a match) evaluates to true or false

2. both pattern and ctexpr must be true

3. symbols in pattern are substituted with concrete types when evaluating ctexpr

4. symbols can have fixed types (e.g S1=int). If reasonable when evaluating
pattern symbols can have also value:
e.g T U N=uint : U[N] N>100; // matches all static arrays bigger than 100;
                             // N = number of declared elements in s. array

5. parts of ctexpr can be omitted, so it looks exactly like current
declarations (mostly)
e.g. T : bool; // omitted symbols and ctexpr
     T;        // omitted symbols, ":", pattern, ctexpr

6. initial T can not be redefined; currently it is rather confusing that
elephant transforms into monkey in following:
void parse(T : T[]) {}

7. following definition:
void parse(T ELEM : ELEM[], U : bool, V SA N=uint : SA[N] N>100) {}
 should be just syntactic sugar for:
void parse(is(T ELEM : ELEM[]) && is(U : bool) && is(V SA N=uint : SA[N]
N>100)) {}
Don't worry about long function template definition - above example is for
extreme hard core metaprogrammers! Proposed syntax is no longer than
current one - it just extends possibilities.

8. ... eventually is() for metaprogramming could be dropped.

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

10. if inexpr is not defined T is aliased to all symbols

Well, I probably missed something here, but IMHO it is just a matter of refining of this proposal...  :-)

-----------------

With above proposal there would be only one syntax for: is(), static if, template definition & static assert

I think that there is also often need in metaprogramming just to decompose type without using value of expression at all. Currently decomposition is possible only in is() and in template declaration (somewhere else?...).

To get advantage of type decomposition without using is() and template declaration I propose to use alias.

Alias could have EXACTLY same syntax as for other meta constructs without breaking current behaviour. How is it possible? See below:

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

1. if ctexpr in alias evaluates to false compilation is stopped with error message.

2. currently we have only
alias T U;
which means T is aliased as U

3. in extended alias definition you would be able to define few symbols instead of one which not necessarily are same as original T.

4. current syntax is just subpattern of new syntax

-----------------

Why my proposal is argument for using alias instead of enum for manifest constants? See following:

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

here type T is decomposed into:
U - static array element type
N - number of elements in static array

In this example N is just *** manifest constant *** for uint value.

IMHO it's good argument to use alias keyword for manifest constants.


-- 

February 12, 2008
http://d.puremagic.com/issues/show_bug.cgi?id=1827





------- Comment #1 from andrei@metalanguage.com  2008-02-12 04:15 -------
(In reply to comment #0)
> I decided to put my proposal from news list (posted some time ago) as
>  I think it can really improve current situation with templates.
> After implementing quite a big piece of template code I can only say
> that currently the whole system is quite inconsistent, but situation
> can be easily improved, keeping a lot of backward compatibility.

Thanks for your thoughts. They closely reflect some ideas that I brought forth with Walter a short while ago.

> I did not get any replays for my proposals and it's difficult for me to say why: - proposal is so bad? - proposal is so good, so there is no need to comment on it? - no one is interested?

Where was the proposal?

> --- I wonder if you ever try to define something like this:
> 
> void parse(T : T == char[] || T == wchar[] || T == dchar[])(T str) {
>  }

You mean match a type if a Boolean condition is satisfied? Yes, I also felt a need for that.

> or match associative array in template declaration?

Well this works today.

> Or maybe you got annoyed with different syntaxes for template definitions and is() expression?
> 
> static if (is(T : U[N], U, size_t N)) ...; //ok
> void parse(T : U[N], U, size_t N) ...; //doesn't work
> void parse(T U: U*)(T val) {};
> //doesn't work

I think the intent is that at least the first two work.

> or wonder why symbol T gets redefined into array element like below:
>  void parse(T : T[]) {}

This is bar none the fugliest hack ever since eunuchs have been invented. I have argued many times with Walter to remove it, and probably de facto it will - none of the book's code samples will feature it.

> or wants to define template like below: void parse(T : isString!(T)) {}

This is again the case of instantiating a template depending on a Boolean condition.

> If yes, please read on.
[snip]

I think the proposal has good merit. I like the separation between introduced types and constraints. The separation is diminished by the presence of =T constraints on the left-hand side of ":".

The syntax we had in mind, that would also apply to templates, is(), etc., is:

T if condition

where "condition" is a compile-time expression that must evaluate to true if T is to be matched. Then you'd write:

S toupper(S if isSomeString!(S))(S input) { ... }

Walter likes this. I'm not sure what he'd say about your syntax; my guess is that he'll take the path of least resistance. I don't know what that is :o).


Andrei


-- 

February 12, 2008
http://d.puremagic.com/issues/show_bug.cgi?id=1827





------- Comment #2 from aarti@interia.pl  2008-02-12 05:26 -------
(In reply to comment #1)
> Where was the proposal?

I posted it on digitalmars.D. But it was early Sunday morning (at least in
Europe), so probably everyone was too sleepy to read this lengthy post :-)

>> static if (is(T : U[N], U, size_t N)) ...; //ok
>> void parse(T : U[N], U, size_t N) ...; //doesn't work
>> void parse(T U: U*)(T val) {};
>> //doesn't work
>I think the intent is that at least the first two work.

Matching in template function pointers can be also useful sometimes. I just finished serialization library for D (http://dsource.org/projects/doost/browser/trunk/doost/util/serializer) and I had to deal sometimes with very exotic types.

I found e.g. that there is no way to create pointer to class on heap in D.
(see: http://d.puremagic.com/issues/show_bug.cgi?id=1778)
I think that it would be possible to overcome this problem, but I must find a
little bit more time to rethink it. You can find my initial thoughts in bug
report...

> I think the proposal has good merit. I like the separation between introduced types and constraints. The separation is diminished by the presence of =T constraints on the left-hand side of ":".

Well maybe it would be even better to put =T on right side of ":". E.g. like
this:
T E K : E[K=uint]
I have not clear opinion on that. Both versions have their pros & cons.

> The syntax we had in mind, that would also apply to templates, is(), etc., is:

> T if condition

> where "condition" is a compile-time expression that must evaluate to true if T
> is to be matched. Then you'd write:
> S toupper(S if isSomeString!(S))(S input) { ... }

Drawback of this syntax is that it will completely break source compatibility.
Using ":" as separator
and defining symbols before ":" will allow to keep quite a lot of code
completely unchanged. Given the fact that
proposed syntax can be reduced to simpler versions, it should match a lot of
current syntax e.g.
void parse(T); void parse(T : int); is(T U : U*); is(T E : E[])

-----

I am very curious what do you think about my 'alias' part of proposal? I found a few places in my code where I wanted just to decompose type without checking condition (condition was already checked before). But it is currently not possible...

And what do you think about 'alias' as a way to define manifest constant?

Initially I liked most 'alias', then when Walter decided to use 'enum' I thought: "Ok. Enum is not so bad, as enums are definitely too simplistic for higher level language.". But now, when I discovered the case with static array, where N should be defined as unsigned integer value, I think that alias is much better for that purpose. And 'enum' can be extended in better ways with other features...


-- 

February 12, 2008
http://d.puremagic.com/issues/show_bug.cgi?id=1827





------- Comment #3 from aarti@interia.pl  2008-02-12 06:52 -------
continuation for 'alias' thing...

Templates already currently can evaluate to one of two:
1. manifest constant
2. type (symbol)

---------------------

Template which evaluates to manifest constant:

template isRefType(T) {
    static if (is(T TYPE == TYPE*) || is(T == class))
        const isRefType = true; // or alias true isRefType;
    else
        const isRefType = false;  // or alias false isRefType;
}

Unfortunately syntax:

alias true isRefType;

is not very readable, so maybe it would be worth to introduce inverted syntax when only one symbol is defined:

alias isRefType = true;

as alternative...

---------------------

It would be in pair with templates which evaluates to type:

Template which evaluates to type:

template arrayElementType(T) {
    static if( is( T U : U[] ) )
        alias U arrayElementType;
    else
        static assert(false, "'" ~ T.stringof ~ "' is not an array.");
}

BTW. with alias syntax above template will be not necessary at all, as it would be probably easier to write:

alias T U : U[];


-- 

February 12, 2008
http://d.puremagic.com/issues/show_bug.cgi?id=1827





------- Comment #4 from andrei@metalanguage.com  2008-02-12 11:13 -------
(In reply to comment #2)
> > The syntax we had in mind, that would also apply to templates, is(), etc., is:
> 
> > T if condition
> 
> > where "condition" is a compile-time expression that must evaluate to true if T
> > is to be matched. Then you'd write:
> > S toupper(S if isSomeString!(S))(S input) { ... }
> 
> Drawback of this syntax is that it will completely break source compatibility.

Nonono, the ":" stays. In general, a type constraint looks like this:

T [: pattern] [if condition]

Both the : pattern and the if condition may or may not be there

> -----
> 
> I am very curious what do you think about my 'alias' part of proposal? I found a few places in my code where I wanted just to decompose type without checking condition (condition was already checked before). But it is currently not possible...
> 
> And what do you think about 'alias' as a way to define manifest constant?
> 
> Initially I liked most 'alias', then when Walter decided to use 'enum' I thought: "Ok. Enum is not so bad, as enums are definitely too simplistic for higher level language.". But now, when I discovered the case with static array, where N should be defined as unsigned integer value, I think that alias is much better for that purpose. And 'enum' can be extended in better ways with other features...

Walter wanted to introduce parameterized alias for quite a while, as they are a very often-seen pattern in code. Since, as you noted, they were hard to read, the notation

alias symbol(patterns) = replacement;

would be needed.


Andrei


-- 

February 13, 2008
d-bugmail@puremagic.com wrote:
> http://d.puremagic.com/issues/show_bug.cgi?id=1827
> 
>            Summary: Uniform syntax for is(), static if, alias, template
>                     definition & static assert
>            Product: D
>            Version: unspecified
>           Platform: PC
>         OS/Version: All
>             Status: NEW
>           Severity: enhancement
>           Priority: P2
>          Component: DMD
>         AssignedTo: bugzilla@digitalmars.com
>         ReportedBy: aarti@interia.pl
> 
> 
> I decided to put my proposal from news list (posted some time ago) as I think
> it can really improve current situation with templates. After implementing
> quite a big piece of template code I can only say that currently the whole
> system is quite inconsistent, but situation can be easily improved, keeping a
> lot of backward compatibility.
> 
> I did not get any replays for my proposals and it's difficult for me to say
> why:
> - proposal is so bad?
> - proposal is so good, so there is no need to comment on it?
> - no one is interested?

More likely too dense and too hard to understand on a Sunday morning. :-) Anything that involves the current template specialization syntax or is() syntax just makes my head spin.  Too many meaningless punctuations and meaningless variable names strung together with special implied meanings.

And secondly, maybe some of us remember discussions about the template syntax topic before.  I recall that Andrei had agreed that the current syntax sucked but said he was unable to convince Walter to change it. If Andrei can't convince Walter that the syntax needs to be changed then who can?  There seems little point in debating it.

If Andrei is finally gaining some traction in getting it changed then hooray!

--bb
February 13, 2008
The nastiest hack that I see is

    template MyTemplate(T:T)

Hands up everyone who knows what that means, and in what way  (T:T) is
different from (T)? If you don't know, can you guess?

Well, the answer is that (T) will match int, const(int) and
invariant(int), but in all three cases, T will be int. Whereas, (T:T)
will match the same three things, but now T will be int, const(int)
and invariant(int) respectively.

It's a nasty hack, because, even when you've learned it, you still can't do much with it. It doesn't generalize. You can't say "match only arrays, but preserve constancy", for example.

There are also circumstances where it doesn't work. I never nailed it down to the obligatory ten line example, but I have encountered a situation in my own development where I was using a string mixin with a template, and no matter what I did, I just couldn't get the constancy transferred to where it needed to go.

Something else that gets lost in the translation to template parameters is the referentiality of the original. If the original is "ref", "in", "out" or "inout", that part of the description is not communicated to T. I haven't actually needed this, but I can certainly imagine that one day I might want to.

Similarly, it would be nice to know whether T was public, private, protected or whatever. It would be nice to know if it was static. In the future, it will be nice to know if it's pure.

Essentially, I would like for the template code to somehow be able to "get at", and test for, all of the metadata about the original type - and preferably with a better syntax than (T:T).

Thanks for listening
April 08, 2011
http://d.puremagic.com/issues/show_bug.cgi?id=1827


Trass3r <mrmocool@gmx.de> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |mrmocool@gmx.de


--- Comment #6 from Trass3r <mrmocool@gmx.de> 2011-04-08 06:20:17 PDT ---
(In reply to comment #1)
> > --- I wonder if you ever try to define something like this:
> > 
> > void parse(T : T == char[] || T == wchar[] || T == dchar[])(T str) {
> >  }
> 
> You mean match a type if a Boolean condition is satisfied? Yes, I also felt a need for that.

Has this lead to template contraints?


> > or wonder why symbol T gets redefined into array element like below:
> >  void parse(T : T[]) {}
> 
> This is bar none the fugliest hack ever since eunuchs have been invented. I have argued many times with Walter to remove it, and probably de facto it will - none of the book's code samples will feature it.

Any updates?

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
January 05, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=1827



--- Comment #7 from Trass3r <mrmocool@gmx.de> 2012-01-05 14:01:49 PST ---
(In reply to comment #5)
> The nastiest hack that I see is
> 
>     template MyTemplate(T:T)
> 
> Hands up everyone who knows what that means, and in what way  (T:T) is
> different from (T)? If you don't know, can you guess?
> 
> Well, the answer is that (T) will match int, const(int) and
> invariant(int), but in all three cases, T will be int. Whereas, (T:T)
> will match the same three things, but now T will be int, const(int)
> and invariant(int) respectively.

At least that's not true anymore.

template Bla(T:T)
{
    pragma(msg, T);
}

template Blub(T)
{
    pragma(msg, T);
}

alias Bla!(int) A;
alias Bla!(const int) B;
alias Bla!(immutable int) C;

alias Blub!(int) D;
alias Blub!(const int) E;
alias Blub!(immutable int) F;

$ dmd -c test7.d
int
const(int)
immutable(int)
int
const(int)
immutable(int)

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------