Thread overview
Semantics of mixed CT and RT statements
Dec 22, 2018
Timoses
Dec 22, 2018
Rubn
Dec 22, 2018
Timoses
Dec 22, 2018
NaN
Dec 23, 2018
Timoses
Dec 23, 2018
Neia Neutuladh
Dec 24, 2018
Timoses
Dec 24, 2018
Neia Neutuladh
Dec 24, 2018
NaN
Dec 24, 2018
NaN
December 22, 2018
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?

December 22, 2018
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");
    }
}
December 22, 2018
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");
>     }
> }



December 22, 2018
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.


December 23, 2018
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.
December 23, 2018
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.
December 24, 2018
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.


December 24, 2018
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");
  }
}
December 24, 2018
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.



December 24, 2018
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