www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - guard clause style static if

reply kdevel <kdevel vogtner.de> writes:
It appears not to be possible to use static if in "guard clause 
style" as in

    void bar (T ...) (T args)
    {
       static if (args.length == 0)
          return;

       writeln (args [0]);
       return bar (args [1 .. $]);
    }

Is this intended?
Jul 07 2018
next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 07/07/2018 11:28 PM, kdevel wrote:
 It appears not to be possible to use static if in "guard clause style" 
 as in
 
     void bar (T ...) (T args)
     {
        static if (args.length == 0)
           return;
else {
 
        writeln (args [0]);
        return bar (args [1 .. $]);
}
     }
Jul 07 2018
parent reply kdevel <kdevel vogtner.de> writes:
On Saturday, 7 July 2018 at 11:29:35 UTC, rikki cattermole wrote:
        static if (args.length == 0)
           return;
else {
 
        writeln (args [0]);
        return bar (args [1 .. $]);
}
That's not guard clause style [1][2]. [1] https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html [2] http://wiki.c2.com/?GuardClause
Jul 07 2018
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 07/07/2018 11:44 PM, kdevel wrote:
 On Saturday, 7 July 2018 at 11:29:35 UTC, rikki cattermole wrote:
        static if (args.length == 0)
           return;
else {
        writeln (args [0]);
        return bar (args [1 .. $]);
}
That's not guard clause style [1][2]. [1] https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html [2] http://wiki.c2.com/?GuardClause
Neither was your original example. "A method has conditional behavior that does not make clear what the normal path of execution is" void bar (T ...) (T args) if (T.length == 0) { return; writeln (args [0]); return bar (args [1 .. $]); } void bar (T ...) (T args) if (T.length > 0) { writeln (args [0]); return bar (args [1 .. $]); } (you meant T I suspect, as args is a runtime thing, but T is compile time, just like static if).
Jul 07 2018
next sibling parent reply kdevel <kdevel vogtner.de> writes:
On Saturday, 7 July 2018 at 11:56:40 UTC, rikki cattermole wrote:
 On 07/07/2018 11:44 PM, kdevel wrote:
 On Saturday, 7 July 2018 at 11:29:35 UTC, rikki cattermole 
 wrote:
        static if (args.length == 0)
           return;
else {
        writeln (args [0]);
        return bar (args [1 .. $]);
}
That's not guard clause style [1][2]. [1] https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html [2] http://wiki.c2.com/?GuardClause
Neither was your original example.
My example was void bar (T ...) (T args) { static if (args.length == 0) // 3 return; // 4 writeln (args [0]); return bar (args [1 .. $]); } The guard clause is in lines 3 and 4.
 "A method has conditional behavior that does not make clear 
 what the normal path of execution is"
In my example one immediately spots the "normal path of execution". Your proposal void bar (T ...) (T args) { static if (args.length == 0) return; else { writeln (args [0]); return bar (args [1 .. $]); } } leads to unreadable arrow code [3].
    void bar (T ...) (T args) if (T.length == 0)
    {
       return;
[removed]
    }

    void bar (T ...) (T args) if (T.length > 0)
    {
       writeln (args [0]);
       return bar (args [1 .. $]);
    }
Interesting alternative but using template constraints introduces code repetition: If you want to add another special case, say length == 4, you have to repeat the logical complement in the "else" branch: void bar (T ...) (T args) if (T.length == 0) { return; } void bar (T ...) (T args) if (T.length == 4) { // some code } void bar (T ...) (T args) if (T.length > 0 && T.length != 4) { writeln (args [0]); return bar (args [1 .. $]); } [3] https://blog.codinghorror.com/flattening-arrow-code/
Jul 07 2018
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 08/07/2018 12:40 AM, kdevel wrote:
 Interesting alternative
That was not an alternative. That is what your code was doing.
Jul 07 2018
parent reply kdevel <kdevel vogtner.de> writes:
On Saturday, 7 July 2018 at 12:46:08 UTC, rikki cattermole wrote:
 On 08/07/2018 12:40 AM, kdevel wrote:
 Interesting alternative
That was not an alternative. That is what your code was doing.
What my original code was supposed to do. But it did not compile. Error: array index [0] is outside array bounds [0 .. 0] Error: string slice [1 .. 0] is out of bounds My question is if it is intentionally failing to compile a static if guard clause.
Jul 07 2018
next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 08/07/2018 12:54 AM, kdevel wrote:
 On Saturday, 7 July 2018 at 12:46:08 UTC, rikki cattermole wrote:
 On 08/07/2018 12:40 AM, kdevel wrote:
 Interesting alternative
That was not an alternative. That is what your code was doing.
What my original code was supposed to do. But it did not compile.    Error: array index [0] is outside array bounds [0 .. 0]    Error: string slice [1 .. 0] is out of bounds My question is if it is intentionally failing to compile a static if guard clause.
There is no such thing as a static if guard clause. static if does not exist at runtime, only compile time. So when you erase it (CT -> RT)... void func() { static if(true) { return; } func2(); } becomes: void func() { return; func2(); } Which is clearly an error. Hence why you need to add else block.
Jul 07 2018
parent reply kdevel <kdevel vogtner.de> writes:
On Saturday, 7 July 2018 at 13:03:32 UTC, rikki cattermole wrote:
 void func() {
 	return;

 	func2();
 }

 Which is clearly an error. Hence why you need to add else block.
There is no error in this generated code because func2 is unreachable. That there is a state/stage during compilation in which the call to func2 exists and is checked is an implementation detail which IMHO shall not be exposed to the user.
Jul 10 2018
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, 10 July 2018 05:52:59 MDT kdevel via Digitalmars-d-learn wrote:
 On Saturday, 7 July 2018 at 13:03:32 UTC, rikki cattermole wrote:
 void func() {

     return;

     func2();

 }

 Which is clearly an error. Hence why you need to add else block.
There is no error in this generated code because func2 is unreachable.
If you don't get an error with code like void func() { return; func2(); } then you're not compiling with -w. A _lot_ of projects compile with -w (IIRC, it's the default for dub). So, unless you're dealing with code that only you are going to be using, and you can guarantee that it will never be compiled with -w, then you're going to have problems with code like this. If you want to ensure that your code is going to be usable by other folks, then you should be compiling with -w.
 That there is a state/stage during compilation in
 which the call to func2 exists and is checked is an
 implementation detail which IMHO shall not be exposed to the user.
It's going to matter if your code doesn't compile, since at that point, the implementation detail impacts the user. And unreachable code is something that won't compile for most projects. - Jonathan M Davis
Jul 10 2018
prev sibling parent reply Alex <sascha.orlov gmail.com> writes:
On Saturday, 7 July 2018 at 12:54:03 UTC, kdevel wrote:
 On Saturday, 7 July 2018 at 12:46:08 UTC, rikki cattermole 
 wrote:
 On 08/07/2018 12:40 AM, kdevel wrote:
 Interesting alternative
That was not an alternative. That is what your code was doing.
What my original code was supposed to do. But it did not compile. Error: array index [0] is outside array bounds [0 .. 0] Error: string slice [1 .. 0] is out of bounds My question is if it is intentionally failing to compile a static if guard clause.
The site you cited for the guard clause above (c2.com) works at runtime. The intention is to shorten the paths inside a function, I think. Therefore, a static "guard clause" is a contradiction, if I understand it correctly. The constraint in form fun(...)(...) if(...) is a static construct. Therefore, all parts should be mentioned statically. And by https://dlang.org/spec/version.html#staticif paragraph: 24.5.4.2 a static if does not introduce a new scope. So, the argument about arrow code is not valid here.
Jul 07 2018
parent reply kdevel <kdevel vogtner.de> writes:
On Saturday, 7 July 2018 at 13:12:59 UTC, Alex wrote:
 The site you cited for the guard clause above (c2.com)
 works at runtime.
?
 The intention is to shorten the paths inside a function, I 
 think. Therefore, a static "guard clause" is a contradiction, 
 if I understand it correctly.
The term "guard clause" denotes a style of organizing code differently. The addressee is not the compiler but the human.
Jul 10 2018
parent Timoses <timosesu gmail.com> writes:
On Tuesday, 10 July 2018 at 12:05:11 UTC, kdevel wrote:
 On Saturday, 7 July 2018 at 13:12:59 UTC, Alex wrote:
 The site you cited for the guard clause above (c2.com)
 works at runtime.
?
static if works at compile team and only inserts code into the final code for run-time depending on the condition (which has to be known at compile time). In your case void bar (T ...) (T args) { static if (args.length == 0) return; writeln (args [0]); return bar (args [1 .. $]); } you could also write static if (T.length == 0) so in case T.length == 0 the resulting run-time code would yield void bar (T args) // in that case T is nothing { return; writeln (args [0]); return bar (args [1 .. $]); } So the only thing you can control with static if statements is "what code to run" depending on the template arguments. The problem with the error messages you are getting (https://forum.dlang.org/post/yndsroswikghknzlxwqi forum.dlang.org) is that the compiler checks during compilation time whether `args[0]` is valid (which in above case it is not). So you can't use args like a "normal array" as you would in control statements during run-time. I don't know what exactly `args` represents in the background.
 [...]
Jul 10 2018
prev sibling parent kdevel <kdevel vogtner.de> writes:
On Saturday, 7 July 2018 at 11:56:40 UTC, rikki cattermole wrote:
    void bar (T ...) (T args) if (T.length == 0)
    {
       return;
[...]
    }

    void bar (T ...) (T args) if (T.length > 0)
    {
       writeln (args [0]);
       return bar (args [1 .. $]);
    }
This is a version without a second condition: import std.stdio; void foo () { writeln; } void foo (T ...) (T args) if (T.length > 0) { writeln ("arg = ", args [0]); foo (args[1 .. $]); } void main () { foo (); foo (1); foo (1, "2"); foo (1, 2, 3.); }
Jul 10 2018
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/7/18 7:28 AM, kdevel wrote:
 It appears not to be possible to use static if in "guard clause style" 
 as in
 
     void bar (T ...) (T args)
     {
        static if (args.length == 0)
           return;
 
        writeln (args [0]);
        return bar (args [1 .. $]);
     }
 
 Is this intended?
Yes. Try just a normal if -- it will have the same effect (the optimizer will eliminate the dead code), but will compile. Of course, you have to fix your second part to only return bar if args.length > 0! -Steve
Jul 09 2018
parent reply kdevel <kdevel vogtner.de> writes:
On Tuesday, 10 July 2018 at 00:11:27 UTC, Steven Schveighoffer 
wrote:
 On 7/7/18 7:28 AM, kdevel wrote:
 It appears not to be possible to use static if in "guard 
 clause style" as in
 
     void bar (T ...) (T args)
     {
        static if (args.length == 0)
           return;
 
        writeln (args [0]);
        return bar (args [1 .. $]);
     }
 
 Is this intended?
Yes. Try just a normal if -- it will have the same effect (the optimizer will eliminate the dead code), but will compile.
I would like to suggest an extension of the language by introducing static return Expression_opt; which shall have the effect of a return plus that the remaining lines in the current block are treated as if they were enclosed in an else block.
 Of course, you have to fix your second part to only return bar 
 if args.length > 0!
This violates DRY. Same problem as with the template constraints version where the condition used in every special case has to be repeated in the general case.
Jul 10 2018
next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, 10 July 2018 05:38:33 MDT kdevel via Digitalmars-d-learn wrote:
 I would like to suggest an extension of the language by
 introducing

      static return Expression_opt;

 which shall have the effect of a return plus that the remaining
 lines in the current block are treated as if they were enclosed
 in an else block.
Well, you can propose it, but it really doesn't fit with how static if works. static if doesn't have anything to do with control flow, whereas what you're proposing here would, which would arguably make it that much more confusing. I confess that I don't understand what the problem is with simply adding an else block. It's simple, and it works right now without any language changes. - Jonathan M Davis
Jul 10 2018
parent Timoses <timosesu gmail.com> writes:
On Tuesday, 10 July 2018 at 12:10:27 UTC, Jonathan M Davis wrote:
 On Tuesday, 10 July 2018 05:38:33 MDT kdevel via 
 Digitalmars-d-learn wrote:
 I would like to suggest an extension of the language by 
 introducing

      static return Expression_opt;

 which shall have the effect of a return plus that the 
 remaining lines in the current block are treated as if they 
 were enclosed in an else block.
Well, you can propose it, but it really doesn't fit with how static if works. static if doesn't have anything to do with control flow, whereas what you're proposing here would, which would arguably make it that much more confusing. I confess that I don't understand what the problem is with simply adding an else block. It's simple, and it works right now without any language changes. - Jonathan M Davis
Actually, it's kind of controlling "compile-time control flow". If you had auto func(T)(T args) { static if (T.length == 0) static return; return args[0]; } then `static return;` would instruct the compiler to skip the rest of the code and only insert a `return;`, omitting any code that follows. So the result in above case with `T.length == 0` would be: void func() { return; } To me that sounds like a very nice feature, considering that I often found myself in the same situation where I didn't feel like the static `else` statement was really necessary (comparing to the usual way to control flow (at run-time) with convential if-else statements).
Jul 10 2018
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/10/18 7:38 AM, kdevel wrote:
 On Tuesday, 10 July 2018 at 00:11:27 UTC, Steven Schveighoffer wrote:
 On 7/7/18 7:28 AM, kdevel wrote:
 It appears not to be possible to use static if in "guard clause 
 style" as in

     void bar (T ...) (T args)
     {
        static if (args.length == 0)
           return;

        writeln (args [0]);
        return bar (args [1 .. $]);
     }

 Is this intended?
Yes. Try just a normal if -- it will have the same effect (the optimizer will eliminate the dead code), but will compile.
I would like to suggest an extension of the language by introducing     static return Expression_opt; which shall have the effect of a return plus that the remaining lines in the current block are treated as if they were enclosed in an else block.
I think it's simpler than that. IMO, any time you have a static if that uses template parameters, it should be treated for purposes of detecting unreachable code as if it were a normal if statement. To the user "unreachable" code means it never can be reached. But that code CAN be reached with different compile-time parameters. Your anecdote is easy to solve, but it's much more difficult to do something like this in a static loop. I don't want to introduce more syntax, this is basically a lint error.
 Of course, you have to fix your second part to only return bar if 
 args.length > 0!
This violates DRY. Same problem as with the template constraints version where the condition used in every special case has to be repeated in the general case.
Sorry, but it has to try to compile this, and args[1 .. $] is invalid at compile time. You can get it back to being DRY by using else. -Steve
Jul 10 2018