Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

returnif, breakif and continueif #2261

Closed
ghost opened this issue Feb 24, 2019 · 41 comments
Closed

returnif, breakif and continueif #2261

ghost opened this issue Feb 24, 2019 · 41 comments

Comments

@ghost
Copy link

ghost commented Feb 24, 2019

I suggest to add conditional terminate statements say:
returnif, breakif and continueif
Or return??, break?? and continue??

Syntax:
returnif Expr1;
returnif (Expr1, Expr2,....., ExprN);
These conditional terminate statements will be executed only if one of the expressions is true or is not null.

Use case:

for (int i = 0; i<N; i++)
{
     returnif Find(i);
}

This is a shortcut for

for (int i = 0; i<N; i++)
{
      var f = Find(i);
      if (f != null) 
          return f
}

Example 2:

        ToolStripItem CheckControls(Control parent)
        {            
            foreach (Control c in parent.Controls)
            {
                if (c.Visible)
                {
                    var t = CheckToolStrip(c as ToolStrip);
                    if (t != null)
                        return t;

                    t = CheckToolStrip(c.ContextMenuStrip);
                    if (t != null)
                        return t;

                    t = CheckControls(c);
                    if (t != null)
                        return t;
                }
            }

            return null;
        }

Would be:

ToolStripItem CheckControls(Control parent)
{            
   foreach (Control c in parent.Controls)
   {
       if (c.Visible)
          returnif (CheckToolStrip(c as ToolStrip), 
                    CheckToolStrip(c.ContextMenuStrip),
                    CheckControls(c));
   }

   return null;
}
@YairHalberstadt
Copy link
Contributor

Should the language have a thousand different keywords, so that you never need to type anything? Replace every single common pattern with a keyword?

The language gives useful building blocks with which it is possible to express almost any logic you want. Adding in keywords willy nilly to save a line of code just increases the cognitive complexity of the language, and the complexity of implementing it.

@CyrusNajmabadi
Copy link
Member

for (int i = 0; i<N; i++)
{
     returnif Find(i);
}

You can already write that as:

for (int i = 0; i<N; i++)
{
     if (Find(i)) return;
}

There appears to be almost no value added by these proposals.

@CyrusNajmabadi
Copy link
Member

Your use of if in a keyword to mean something that checks against something that is not a boolean goes very much against the spirit of C#.

@YairHalberstadt
Copy link
Contributor

@CyrusNajmabadi
I believe returnif is a null check, not a Boolean check

@CyrusNajmabadi
Copy link
Member

You can already write this as:

                    var t = CheckToolStrip(c as ToolStrip);
                    if (t != null)
                        return t;

                    t = CheckToolStrip(c.ContextMenuStrip);
                    if (t != null)
                        return t;

                    t = CheckControls(c);
                    if (t != null)
                        return t;

like so:

                    var t = CheckToolStrip(c as ToolStrip) ??  CheckToolStrip(c.ContextMenuStrip) ?? CheckControls(c);
                    if (t != null)
                        return t;

You are writing out code that is more compelx than necessary, not using available mechanisms to shorten things, and then asking for new features to replace the ones already there.

@CyrusNajmabadi
Copy link
Member

@YairHalberstadt

I believe returnif is a null check, not a Boolean check

addressed here: #2261 (comment)

@CyrusNajmabadi
Copy link
Member

@MohammadHamdyGhanem Instead of:

for (int i = 0; i<N; i++)
{
     returnif Find(i);
}

Why not returnforif i, N, Find(i)?

@CyrusNajmabadi
Copy link
Member

CyrusNajmabadi commented Feb 24, 2019

ToolStripItem CheckControls(Control parent)
{            
   foreach (Control c in parent.Controls)
   {
       if (c.Visible)
          returnif (CheckToolStrip(c as ToolStrip), 
                    CheckToolStrip(c.ContextMenuStrip),
                    CheckControls(c));
   }

   return null;
}

You can already write the above more simply as:

return parent.Controls.OfType<Control>()
                      .Where(c => c.IsVisible)
                      .Select(c => CheckToolStrip(c as ToolStrip) ?? 
                                   CheckToolStrip(c.ContextMenuStrip) ??
                                   CheckControls(c))
                      .WhereNotNull()
                      .FirstOrDefault();

The code i wrote is possible today, and is 7 lines to the 9 lines your feature would offer. You don't need new language features for brevity. You need to use the features the language already provides you.

@ghost
Copy link
Author

ghost commented Feb 24, 2019

@CyrusNajmabadi
let me stop at WhereNotNull! I never saw if before despit reading what's new in every version of VS and its components! I even can't find docs about it!
Why?!

By the way, LinQ expressions can be complex and less readable and harder to understand, so it is not always a good thing to write each procedure as a LinQ query!

@CyrusNajmabadi
Copy link
Member

CyrusNajmabadi commented Feb 25, 2019

I even can't find docs about it!
Why?!

Because it's a trivial method you can write in 10 seconds.

By the way, LinQ expressions can be complex and less readable and harder to understand

I see nothing about your code that is more readable than this simple linq expression which has been supported in the language for nearly 15 years at this point.

Also, this is not a linq quesry. it's just bog standard method calls with lamdbas. Bread and butter of C# for ages. Adding more and more syntax to replace what can be trivially done with existing features is not great...

@CyrusNajmabadi
Copy link
Member

.WhereNotNull()

It's just my shortcut for writing .Where(c => c != null)

@yaakov-h
Copy link
Member

Who needs a .Where?

.FirstOrDefault(c => c != null);

@Thaina
Copy link

Thaina commented Feb 25, 2019

#138 (comment)

@Thaina
Copy link

Thaina commented Feb 25, 2019

let me stop at WhereNotNull! I never saw if before despit reading what's new in every version of VS and its components! I even can't find docs about it!

It don't exist in BCL (yet?) but you can write it yourself anytime

https://github.com/dotnet/corefx/issues/26698

@DavidArno
Copy link

DavidArno commented Feb 25, 2019

As of C# 8, example 2 can be expressed as:

ToolStripItem? CheckControls(Control parent)
{
    foreach (Control c in parent.Controls)
    {
        if (c.Visible &&
            (CheckToolStrip(c as ToolStrip) ??
             CheckToolStrip(c.ContextMenuStrip) ??
             CheckControls(c)) is {} t)
        {
            return t;
        }
    }

    return null;
}

I therefore see no use for a returnif, especially as that if on the end of return is a if not null, which I found incredibly confusing.

@Austin-bryan
Copy link

I believe returnif is a null check, not a Boolean check

While you're probably right, this makes it even worse because we would assume it's a boolean. It also great limits the use case when he could've made the function return true or false or do a returnif (Find(t) == null); but gotta type as little as possible, that's always what makes a feature good, amiright?

@theunrepentantgeek
Copy link

Instead of the proposed returnif, breakif and continueif, which all have an extremely narrow focus based on null values, I'd prefer to see something more flexible and generally useful.

What about reusing the existing when (condition) syntax from conditional catch, along with the value keyword from property setters?

The first example from the OP would become ...

for (int i = 0; i < N; i++)
{
     return Find(i) when (value != null);
}

Example 2 from the OP:

ToolStripItem CheckControls(Control parent)
{            
    foreach (Control c in parent.Controls)
    {
        if (c.Visible)
        {
            return CheckToolStrip(c as ToolStrip) when (value != null); 
            return CheckToolStrip(c.ContextMenuStrip)  when (value != null); 
            return CheckControls(c)  when (value != null); 
        }
    }

    return null;
}

The advantage of this syntax is that the condition isn't hidden - even better, the dev has a choice of condition and doesn't have to contort things.

Though, having written this up ... while this is better (more generally useful) than the original suggestion ... I do not think adding optional when (condition) is a good idea ... the language already has these scenarios handled well.

@YairHalberstadt
Copy link
Contributor

@theunrepentantgeek
What is the advantage of return item when (condition); over if (condition) return item;?

@theunrepentantgeek
Copy link

Very, very, little. 😀

Perhaps that it more naturally sits on one line ... perhaps.

@ghost
Copy link
Author

ghost commented Feb 27, 2019

@theunrepentantgeek
Your suggestion is good, but I want things to be shorter. I had your suggestion in mind from start but in the form:

for (int i = 0; i < N; i++)
{
     if ((var r = Find(i)) != null) 
        return r;
}

It is still longer, but saves one line, and it will be valid as soon as C# allows to declare variables inside any ( ) which is an existing suggestion as I think. The problem is we have to enclose the declaration ((var r = Find(i)) otherwise r acn be consedered a bool var to hold the result of Find(i) != null!
By the way, I want ReturnIf to check expression to be true, ore not null, so it is can be used with any bool condition as will, and I said that from the start:

These conditional terminate statements will be executed only if one of the expressions is true or is not null.

The rulse are simple:

  • If expression is boolean, return if true.
  • If expression is nullable boolean or an object, return if not null.

So, my suggestion achives your aim with minimal syntax.
Thanks.

@CyrusNajmabadi
Copy link
Member

The problem is

This doesn't seem like a problem.

It is still longer, but saves one line,

You could save all lines if you just added returnforif i, N, Find(i).

I think you overly obsess over taking each and every bit of code you write, and trying to make it into a single line. You do this in many different issues, and you commonly ignore existing constructs that will simplify your code, asking for new constructs instead because you just don't like something about the existing constructs.

This isn't a tenable way to do language design. We can't just add lots of orthogonal features to improve things, have them ignored, and then have proposals taht are much more limited in scope just because you won't use the existing features.

  1. If expression is boolean, return if true.
  2. If expression is nullable boolean or an object, return if not null.

This very much goes against hte spirit of C#. We do not have flow control operators that are interchangeable over bools and nulls. Indeed, it's been explicit to not do this due to the common concern about bugs creeping in this way. This would not be done without a major philosophical shift in the language. And if that were to happen, you'd see it first in cases like if (foo) where we would now allow null in that location instead of just boolean. Heck, we don't even allow interchangable flow control over bool and Nullable<bool>. So there is zero chance we'd do it for bool Nullable<bool> and null.

@DavidArno
Copy link

DavidArno commented Feb 27, 2019

@MohammadHamdyGhanem,

You code example,

for (int i = 0; i < N; i++)
{
     if ((var r = Find(i)) != null) 
        return r;
}

is already valid in C# 8, just with slightly different syntax:

for (int i = 0; i < N; i++)
{
     if (Find(i) is {} r) 
        return r;
}

So it still remains unclear to me as to what you are really wanting here.

@ghost
Copy link
Author

ghost commented Feb 27, 2019

@DavidArno
if (Find(i) is {} r)
I saw that in whats new, and didn't try to decipher it! This is the kind of syntax that I will never write, and will have difficult times tring to understand what it means every time I read it. Couldn't you at least make it: if (Find(i) is var r) ? or better: if (Find(i) as r).
I know that if (Find(i) is r) has another meaning, so you have to come with a new syntax, but {} is a very strange notation in a strange place! Even if (Find(i) r) or if (Find(i) : r) can be better!
I like shorter code, giving it is readable. My goal in all my issues is not just to make code shorter, but smarter: Write less , read easily.

@CyrusNajmabadi
Copy link
Member

Couldn't you at least make it: if (Find(i) is var r)

You can write that. But that will not have the != null semantics you want. That's because var can absolutely be null, and as such it's the "i will always match pattern".

@CyrusNajmabadi
Copy link
Member

I saw that in whats new, and didn't try to decipher it!

Again, this is what i was saying above. You are not using the actual facilities given in hte language, and are asking for new language features to implement a more limited form of functionality.

@CyrusNajmabadi
Copy link
Member

CyrusNajmabadi commented Feb 27, 2019

but {} is a very strange notation in a strange place!

Why is it strange?

if (Find(i) : r) can be better!

You save one character. All to do something the language can already do today. at this point, these proposals just seem like spam. You avoid the features the language has, and you continually propose alternate ways to just do the exact same thing, but with barely any saving (if any) at all.

and didn't try to decipher it!

Please try to learn all the language has to offer. You might find you need far less changes in it if you didn't continuously try to not learn about and not use what is already there. :-/

@ghost
Copy link
Author

ghost commented Feb 28, 2019

@CyrusNajmabadi
{} has a meaning in C#: it contains a block of code or initial values. What does it mean here?
I don't ask to shorten it. I ask to use something that have meaning!
C# is a LANGUAGE. You can always interpret its syntax to English words. Can you do this for if (Find(i) is {} r)?
Would you say: If find of i is empty block r?
Or: If find of i is zero-initialized r?
This syntax doesn't fit as a familiar C# syntax, C++ syntax or any language I saw before.
So, I will not write such unusual code. I long lived b4 it and I can continue live without it!
Couldn't it be if (Find(i) is {r})?
Or if (Find(i) {is r})?
And frankly, in all patterns, is itself should have been 'as' or 'set' . Every time I saw 'is' in this place I pause and take a some time to understand the meaning of the statement.
if (Find(i) {as r})
if (Find(i) {=> r})
if (Find(i) set r)
The last one feals like C# and is readable in English.

@CyrusNajmabadi
Copy link
Member

{} has a meaning in C#: it contains a block of code or initial values. What does it mean here?

it means a property pattern. i.e. i can do things like if (point is { X: 0, Y: 0 }) { ... } That tests that you have an actual object (i.e. not null), and it has two members called 'X' and 'Y' that match those respective patterns. { } is simply the degenerate case where you are ensuring you have an actual object, but you don't care to test any further.

@CyrusNajmabadi
Copy link
Member

I ask to use something that have meaning!

It has meaning.

C# is a LANGUAGE. You can always interpret its syntax to English words. Can you do this for if (Find(i) is {} r)?

Of course. "Call Find with value i, test the result value against null. If it's non-null, place it in a varaible called 'r' and execute the 'true part' of hte 'if statement'"

This syntax doesn't fit as a familiar C# syntax, C++ syntax or any language I saw before

None of returnif, breakif and continueif fit a familiar c# syntax, c++ syntax, or any language i saw before.

Couldn't it be if (Find(i) is {r})

No.

Or if (Find(i) {is r})?

No.

Or if (Find(i) {is r})

No.

And frankly, in all patterns, is itself should have been 'as' or 'set' . Every time I saw 'is' in this place I pause and take a some time to understand the meaning of the statement.

Why? It's a normal way of saying something. if x is something that matches thsi pattern, then put the value in y and continue. We've had is in the language since C# 1.0. it was simply a type test in the beginning, but now has expanded into the pattern test.

So, I will not write such unusual code. I long lived b4 it and I can continue live without it

You keep asking for features that replicate feature the language already has just because you don't want to use them. This isn't going to happen.

@ghost
Copy link
Author

ghost commented Feb 28, 2019

"Call Find with value i, test the result value against null. If it's non-null, place it in a varaible called 'r' and execute the 'true part' of the 'if statement'"

This is what the compiler does, not what the symbols say in a clear way. Where can I get the non-null meaning in this statement? Empty brackets do give the impression of a null! maybe !{} say non-null (not that I like it, but just saying).
Please note that filling the syntax with such unusual symbols makes the language harder to learn for beginners, and makes writing and reading code harder even for non-beginners.

@ghost
Copy link
Author

ghost commented Feb 28, 2019

And I want to say, that some features staring with multi-line lambdas and local functions complicated the language and make the syntax insane! One can define a local function inside a lambda inside a function, which yields a complex code structure that is hard to comprehend. Why does anyone do that? because he can and C# allows him to!

@HaloFour
Copy link
Contributor

HaloFour commented Feb 28, 2019

not what the symbols say in a clear way

It all becomes clear when you're used to it.

The {} is for a recursive property pattern. You can stick anywhere between 0 and n property patterns in there. It comes after the type pattern, but the type itself may be omitted if it is already known by the compiler. Like a normal type pattern, it only matches when the expression is not null.

The two following patterns do the same thing:

if (Find(i) is Foo r) return r;
if (Find(i) is {} r) return r;

Here's an example of a property pattern being used recursively:

string name = ...;
if (name is { Length: var length }) Console.WriteLine($"The name is {length} characters long.");

@ghost
Copy link
Author

ghost commented Feb 28, 2019

By the way: One thing is missing to reach the full lambda complexity: allow recursive lambdas :D
Func<int, int> Factorial = x => (x <= 1)? 1 : x * this(x-1);

Note:
Away from recursion, suppose I have this:
Func<int, int> foo= x => (x <= 1)? 1 : x;
this short code seems gibberish, and can't be comprehended unless re-written as:

Func<int, int> foo= 
        x => 
                (x <= 1)? 1 :  x;

@ghost
Copy link
Author

ghost commented Feb 28, 2019

@HaloFour
Ok. Let's wait to April to try all these new featurs in the released .NET 2019.
Thanks for the explanation.

@HaloFour
Copy link
Contributor

@MohammadHamdyGhanem

allow recursive lambdas

done:

        Func<int, int> Factorial = null;
        Factorial = x => (x <= 1)? 1 : x * Factorial(x-1);

The problem has nothing to do with lambdas, an unassigned variable cannot reference itself.

Local functions may invoke themselves recursively.

@ghost
Copy link
Author

ghost commented Feb 28, 2019

@HaloFour
I better go to sleep now b4 I loose my mind :D

@CyrusNajmabadi
Copy link
Member

CyrusNajmabadi commented Feb 28, 2019

Where can I get the non-null meaning in this statement?

The same place you get the non-null meaning when someone writes: if (x is Y z). or if (x is Y).

In C# { ... } is literally the is this not null, and does it have these ... members? pattern.

Please note that filling the syntax with such unusual symbols makes the language harder to learn for beginners

But you just suggested a large set of unusual symbol that would make the language harder to learn for beginners. Why do these rules you state not apply to your own suggestions? As i already showed (for example here, your features do not fit in with rules that C# has followed since inception. By adding them, you make the language less consistent and harder to learn.

--

The part you seem to be missing is that C# has added patterns. One way patterns are surface is like so:

x is Pattern

Once you know that, then it becomes easy to figure out what's going on. After the is is a pattern, and you just need to learn the patterns. Once you learn the patterns, it's simple to use. However, you have to want to, and be willing to, learn about these features.

@TKharaishvili
Copy link

TKharaishvili commented Feb 28, 2019

I don't like this idea.

The reason I'm posting this comment is that the author doesn't seem to care about arguments against his proposal, maybe seeing the number of people that disagree with him will have more of an effect.

By the way, if in some alternate universe this somehow gets implemented, I also want yieldif.

P.S. Don't mean to be cynical, just think it sounds funny.

@ghost
Copy link
Author

ghost commented Feb 28, 2019

@TKharaishvili
You are welcome, but remember that I don't have the power to enforce this idea upon C#, so refusing it can be simply done by just ignoring it.

@jnm2
Copy link
Contributor

jnm2 commented Mar 2, 2019

I think this is close enough to be called a duplicate of #724.

Very similar: #325, #453, #1493.

@YairHalberstadt
Copy link
Contributor

YairHalberstadt commented Oct 22, 2020

Closing as OP no longer has an account on github, and this suggestion was fairly poorly received.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants