www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Semantics of mixed CT and RT statements

reply Timoses <timosesu gmail.com> writes:
I've just had the idea of mixing both `static` and "RT" 
statements:


     void foo (T)(bool b)
     {
         static if (is(T == SomeType)) || if (b)
         {
             // Called when T is of type SomeType
             // otherwise, use `if (b)` for runtime to decide
             // fate of this block's execution
             assert(is(T == SomeType) || b);
             writeln("1");
         }
         else static if (is(T == SomeOtherType)) && if (b)
         {
             // Called when T is of type SomeOtherType
             // AND when b is true
             assert(is(T == SomeOtherType) && b);
             writeln("2");
         }
     }

     foo!SomeType(false); // prints 1
     foo!SomeOtherType(false); // prints nothing
     foo!SomeOtherType(true); // prints 2


static if (A) || if (B): Only ever check B at run-time when A is 
false at compile-time

static (A) && if (B): Check B at run-time if and only if A holds 
true at compile-time.


And here both variants (as had to be implemented currently versus 
the shorter "mixed" variant)

     // Current
     static if (is(T == SomeType))
         // do A
     if (b)
         // do A
     static if (is(T == SomeOtherType))
     {
         if (b)
             // do B
     }

     // vs "Mixed"
     static if (is(T == SomeType)) || if (b)
         // do A
     static if (is(T == SomeOtherType)) && if (b)
         // do B


I don't have a specific use case at hand. I believe it could help 
reduce code duplication in some situations; apparently more so 
for the `||` variant, as the `&&` variant can be rewritten as:

     static if (Cond1) if (Cond2)

and works already.


What do you think?
Dec 21 2018
parent reply Rubn <where is.this> writes:
On Saturday, 22 December 2018 at 03:35:56 UTC, Timoses wrote:
 I've just had the idea of mixing both `static` and "RT" 
 statements:


     void foo (T)(bool b)
     {
         static if (is(T == SomeType)) || if (b)
         {
             // Called when T is of type SomeType
             // otherwise, use `if (b)` for runtime to decide
             // fate of this block's execution
             assert(is(T == SomeType) || b);
             writeln("1");
         }
         else static if (is(T == SomeOtherType)) && if (b)
         {
             // Called when T is of type SomeOtherType
             // AND when b is true
             assert(is(T == SomeOtherType) && b);
             writeln("2");
         }
     }

     foo!SomeType(false); // prints 1
     foo!SomeOtherType(false); // prints nothing
     foo!SomeOtherType(true); // prints 2


 static if (A) || if (B): Only ever check B at run-time when A 
 is false at compile-time

 static (A) && if (B): Check B at run-time if and only if A 
 holds true at compile-time.


 And here both variants (as had to be implemented currently 
 versus the shorter "mixed" variant)

     // Current
     static if (is(T == SomeType))
         // do A
     if (b)
         // do A
     static if (is(T == SomeOtherType))
     {
         if (b)
             // do B
     }

     // vs "Mixed"
     static if (is(T == SomeType)) || if (b)
         // do A
     static if (is(T == SomeOtherType)) && if (b)
         // do B


 I don't have a specific use case at hand. I believe it could 
 help reduce code duplication in some situations; apparently 
 more so for the `||` variant, as the `&&` variant can be 
 rewritten as:

     static if (Cond1) if (Cond2)

 and works already.


 What do you think?
Don't really see the point, the following will always be true because of the static if.
         static if (is(T == SomeType)) || if (b)
         {
             // Called when T is of type SomeType
             // otherwise, use `if (b)` for runtime to decide
             // fate of this block's execution
             assert(is(T == SomeType) || b);
             writeln("1");
         }
You don't need to do another is() inside of the assert, as it is guaranteed to be true. Just put the logic in the static if like normal. static if( is(T== SomeType) ) { // runtime condition if( true || b ) // just becomes true writeln("1"); } else static if( is (T == SomeOtherType ) ) { if( b ) { writeln("2"); } }
Dec 21 2018
parent reply Timoses <timosesu gmail.com> writes:
On Saturday, 22 December 2018 at 03:51:58 UTC, Rubn wrote:
 You don't need to do another is() inside of the assert, as it 
 is guaranteed to be true. Just put the logic in the static if 
 like normal.

 static if( is(T== SomeType) ) {
     // runtime condition
     if( true || b ) // just becomes true
         writeln("1");
 }
This wouldn't print "1" in case T is != SomeType. Hence `static if || if`. Note that the idea of `static if || if` does not exist (afaik):
 static if (A) || if (B): Only ever check B at run-time when A 
 is false at compile-time
 else static if( is (T == SomeOtherType ) ) {
     if( b ) {
         writeln("2");
     }
 }
Dec 21 2018
parent reply NaN <divide by.zero> writes:
On Saturday, 22 December 2018 at 04:02:46 UTC, Timoses wrote:
 On Saturday, 22 December 2018 at 03:51:58 UTC, Rubn wrote:
 You don't need to do another is() inside of the assert, as it 
 is guaranteed to be true. Just put the logic in the static if 
 like normal.

 static if( is(T== SomeType) ) {
     // runtime condition
     if( true || b ) // just becomes true
         writeln("1");
 }
This wouldn't print "1" in case T is != SomeType. Hence `static if || if`.
Just do if ((is(T == SomeType)) || (b)) First bit will be evaluated at compile time but or-ed with 'b' at runtime. The point is if you want the whole thing evaluated in one rather than in sequence, then the whole expression is dependant on a runtime variable, which makes using static if on half of it wrong.
Dec 22 2018
parent reply Timoses <timosesu gmail.com> writes:
On Saturday, 22 December 2018 at 10:29:08 UTC, NaN wrote:
 Just do

 if ((is(T == SomeType)) || (b))

 First bit will be evaluated at compile time but or-ed with 'b' 
 at runtime.

 The point is if you want the whole thing evaluated in one 
 rather than in sequence, then the whole expression is dependant 
 on a runtime variable, which makes using static if on half of 
 it wrong.
I'm not asking for "How to do this now?". I'm just communicating an idea and am curious about your thoughts about it. The point is that
 if ((is(T == SomeType)) || (b))
misses the point of statically generating content IN CASE a condition is true at compile time. static if (T) // do A else if (b) // this already looks a bit like mixing static and run-time // do A The intention is to only do a check of b at run-time when T is false. If T is true, there is no need to check anything at run-time. However, the above has redundant duplicate statement '// do A'. Hence: Mixing static if and RT sounds alluring (the syntax is made up, but tells the story, I believe. Got a better idea?): static if (T) || if (b) // do A The ability of D to write generic code with static statements is amazing already. The question is: Could it be even richer and swifter? The case for the following (again: made up syntax!) static if (T) && if (b) // do A equals the very short static if (T) if (b) // do A This thread is merely of explorative nature and does not suggest any concrete improvements or nags about lack of any existing features.
Dec 22 2018
parent reply Neia Neutuladh <neia ikeran.org> writes:
On Sun, 23 Dec 2018 00:52:23 +0000, Timoses wrote:
 The point is that
 
 if ((is(T == SomeType)) || (b))
misses the point of statically generating content IN CASE a condition is true at compile time.
An optimizer can very easily optimize this: // T is SomeType if (is(T == SomeType) || b) doStuff; to remove the if statement. So from that perspective, no need to worry about it. The sticky bit is that a runtime check needs both paths to typecheck, so if you're mixing metaprogramming with runtime logic, some cases might be weird. I've never needed to do that.
Dec 22 2018
parent reply Timoses <timosesu gmail.com> writes:
On Sunday, 23 December 2018 at 02:58:30 UTC, Neia Neutuladh wrote:
 An optimizer can very easily optimize this:

     // T is SomeType
     if (is(T == SomeType) || b) doStuff;

 to remove the if statement. So from that perspective, no need 
 to worry about it.
Good point. I guess that eliminates the need for something like "static if (T) || if (b)". However, going along that line, why does D even have "static if"? We could simply use "if" everywhere and let the compiler take care of it. Perhaps it's more a question of expressiveness.
 The sticky bit is that a runtime check needs both paths to 
 typecheck, so if you're mixing metaprogramming with runtime 
 logic, some cases might be weird.
Do you have an example? I'm not quite sure I get what you mean.
 I've never needed to do that.
Dec 23 2018
next sibling parent Neia Neutuladh <neia ikeran.org> writes:
On Mon, 24 Dec 2018 01:08:57 +0000, Timoses wrote:
 Do you have an example? I'm not quite sure I get what you mean.
bool foo(T)(T t) if (is(T == int) || is(T == string)) { if (is(T == int) || t == T.init) return false; else return t.startsWith("something"); } This is a pretty contrived example, but the is() constrains the type in the else clause. The else clause has to type-check for both int and string, and it doesn't. The fix is straightforward: detangle the runtime and compile-time conditions. bool foo(T)(T t) if (is(T == int) || is(T == string)) { static if (is(T == int)) { return false; } else // T == string { if (t == T.init) return false; return t.startsWith("something"); } }
Dec 23 2018
prev sibling parent reply NaN <divide by.zero> writes:
On Monday, 24 December 2018 at 01:08:57 UTC, Timoses wrote:
 On Sunday, 23 December 2018 at 02:58:30 UTC, Neia Neutuladh 
 wrote:
 An optimizer can very easily optimize this:

     // T is SomeType
     if (is(T == SomeType) || b) doStuff;

 to remove the if statement. So from that perspective, no need 
 to worry about it.
Good point. I guess that eliminates the need for something like "static if (T) || if (b)". However, going along that line, why does D even have "static if"? We could simply use "if" everywhere and let the compiler take care of it. Perhaps it's more a question of expressiveness.
static if gives you a guarantee it'll be evaluated at run time and the dead code branch pruned away. Not sure if the scoping also has different semantics vs a regular if.
Dec 24 2018
parent NaN <divide by.zero> writes:
On Monday, 24 December 2018 at 10:16:59 UTC, NaN wrote:
 On Monday, 24 December 2018 at 01:08:57 UTC, Timoses wrote:
 On Sunday, 23 December 2018 at 02:58:30 UTC, Neia Neutuladh
static if gives you a guarantee it'll be evaluated at run time and the dead code branch pruned away. Not sure if the scoping also has different semantics vs a regular if.
I meant at "compile time" oops
Dec 24 2018