[Proposal] Labeled loops like in Java #5525
Replies: 114 comments 4 replies
-
@AustinBryan Related #869 |
Beta Was this translation helpful? Give feedback.
-
Also relevant: dotnet/roslyn#5883 |
Beta Was this translation helpful? Give feedback.
-
I like labeled break much better. Also would |
Beta Was this translation helpful? Give feedback.
-
Yes, it should. I just tested it out on Java and it works for that so it should for C#. |
Beta Was this translation helpful? Give feedback.
-
In the last three years I needed only once a |
Beta Was this translation helpful? Give feedback.
-
@AustinBryan I think that your second case was rejected, you can check the post @Joe4evr linked. |
Beta Was this translation helpful? Give feedback.
-
So |
Beta Was this translation helpful? Give feedback.
-
@Neme12 It's clearer about what's going on. More symmetrical with |
Beta Was this translation helpful? Give feedback.
-
@Neme12 I mean yes, except it reflects more on what we want to do. In this example, what I want to do is |
Beta Was this translation helpful? Give feedback.
-
I disagree. the cleanest way is to exit the function, avoiding
If you end up with code that needs a |
Beta Was this translation helpful? Give feedback.
-
I kind of agree - in code reviews, I usually see nested loops as a code smell indicating a need to break out another method. |
Beta Was this translation helpful? Give feedback.
-
@theunrepentantg
Wouldn't that be an extreme? I mean, indeed, sometimes it's the case but saying that you usually tend to find nested loops a code smell when a code smell should indicate that there's a problem with the design not with how the code is structured but more importantly this statement shouldn't be taken as rule of thumb because what's applicable to one codebase might not be applicable to another, one might be more imperative in nature whereas the other can be more declarative and so it really depends. Refactoring too much can also lead to readability and maintainability issues. |
Beta Was this translation helpful? Give feedback.
-
I totally agree - readability, maintainability - and all the 'ilities (scalability, supportability, etc) are very important. Nonetheless, I stand by the statement: Usually nested loops are a design smell.
I don't understand this - how can well-designed code be structured poorly? Design covers all scales - from the macro architecture (components, services, etc) to the micro (algorithm choice, method signatures, local variable naming, etc). |
Beta Was this translation helpful? Give feedback.
-
@theunrepentantgeek I guess that this is where we disagree, you seems to think that nested loops is an indication of a code smell whereas I think that it might be the case but you don't always need to refactor nested loops especially in this case where the language can be improved a bit and make the You can structure the code in many different ways, you can structure it through a series of imperative statements, you can use a more procedural approach, you can use a more declarative approach and apply all kinds of functional programming techniques and yet the design can be broken so just because you refactored something into its own method and you gave it a name and it looks good doesn't mean you improved the design, in fact, it might mean the opposite but really context is important here and in this case the kind of codebase you work with should be considered before going out and tell people what they should or shouldn't do. Now, some people would consider their codebase state of the art because it's refactored and follow awesome set of principles and whatnot only to find out that it's actually poorly designed. |
Beta Was this translation helpful? Give feedback.
-
Such code runs counter to the "code should describe the intent; not the mechanism" principle of easy-to-reason (or "reasonable") code. So if I write code like: var found = false;
for (var i = 0; i < range1; i++)
for (var j = 0; j < range2; j++)
if (i * j == 10)
{
found = true;
goto outer;
}
outer: {} I'm purely expressing the mechanism. The code is highly unreasonable. Therefore the next thing I'm likely to do is add a comment to explain its intent: // loop through the two ranges and report if they contain factors of 10
var found = false;
for (var i = 0; i < 5; i++)
for (var j = 0; j < 3; j++)
if (i * j == 10)
{
found = true;
goto outer;
}
outer: {} Now I have the intent, but I still have the mechanism there too. It's a mess. I'm not sure If it's a code smell, but it certainly is ugly and difficult to read. What I want is the intent only: var found = RangesContainFactorsOfTen(5, 3); The code is now trivially easy to reason. And when I want to understand the mechanism, I head off to that method: private bool RangesContainFactorsOfTen(int range1, int range2)
{
for (var i = 0; i < range1; i++)
for (var j = 0; j < range2; j++)
if (i * j == 10)
return true;
return false;
} So I've achieved code that is far easy to reason and read, I've removed the comment and I've avoided the need for a C# is already complicated enough with a growing number of features with every release. Those new features should be focused on the "pit of success", ie making it easier to write good code. Labelled breaks are the opposite: they just offer another way, in addition to the existing one, of writing bad code. So they they have no place in future versions of the language in my view. |
Beta Was this translation helpful? Give feedback.
-
Understood. Sorry for trivializing your argument hte away i did. |
Beta Was this translation helpful? Give feedback.
-
Jumping back a bit. I can see the argument for why labeled-loops seem to be desirable. They have the following pros (depending on your perspective).
Overall, these are a cogent and clear argument for why such a feature at least "makes sense" in isolation. And, frankly, i'd likely be swayed by this for C# v1 to have this instead of having hte arbitrary-goto feature. Clearly the above (and likely other stuff i missed) is sufficient for some people to really want this even for a v8+ release. I can accept and respect that. -- At this point it really comes down to: Is there an LDM member that thinks the above is sufficient? Or is the alternative we have today (gotos + optional-analyzers) reasonably sufficient (and cost effective) to just stick with the status-quo. My personal opinion is "the status quo is fine". However, not everyone may share htat opinion. We'll just have to see if someone wants to champion this. |
Beta Was this translation helpful? Give feedback.
-
@CyrusNajmabadi All good, thanks for listening!
The latter is better documentation of the loop declaration and feels right. The former feels like a hack, is harder to set up and read, and doesn't contain richer information. Possibly less rich. |
Beta Was this translation helpful? Give feedback.
-
Labels are just a means to an end a way of identifying the loop to which the
or
I'm not advocating that of course but do want to point out that a label is not the only way to provide the information to the |
Beta Was this translation helpful? Give feedback.
-
It's very interesting how different people see these things. I already view a loop as incredibly low-level and imperative. To me your To each their own. |
Beta Was this translation helpful? Give feedback.
-
Huh. It's probably also worth pointing out that I'd only find myself doing this if I was inlining for very specific hot paths. Otherwise I'd use |
Beta Was this translation helpful? Give feedback.
-
That's my problem with it. It documents the loop declaration, not the reasons for wanting to leave the loop. If you have two reasons for wanting to |
Beta Was this translation helpful? Give feedback.
-
@HaloFour That's consistent with how I use |
Beta Was this translation helpful? Give feedback.
-
IN that case, i think it's totally reasonable to just do the same with "goto" :) At this point, i think the arguments have been exhausted. I think i totally get your position @jnm2 . it's consistent and not incorrect in any fashion. It will be up to an LDM member to see if they want to champion.
Seems totally clear to me :D |
Beta Was this translation helpful? Give feedback.
-
Please note that when we talk about cost, it's not just "how hard is this to implement in the compiler"?
You're welcome to do that for you own use but please know that if you plan to submit a PR to Roslyn, you likely won't even be allowed to keep it open or have a feature branch unless this feature is championed. If you find it fun to experiment with this or just implement in case it would get considered in the future, that's awesome but don't be disappointed if that doesn't happen. |
Beta Was this translation helpful? Give feedback.
-
I certainly would be interested in making such a change but there's clearly a ramp up time and we know the team already has the expertise for this. What I was trying to convey is that it is probably within my abilities despite my limited knowledge of roslyn, unlike some other changes. I have no idea how the roslyn design ultimately works but one could possibly just implement some form of transformation that transforms this:
into this:
which is why I described this as syntactic sugar earlier. |
Beta Was this translation helpful? Give feedback.
-
Yup. That's how roslyn works already today. Indeed, as IL has no concept of loops at all, all loops in roslyn are ultimately "lowered" into just equivalent forms with appropriate "gotos". So you could certainly implement this feature in such a manner. |
Beta Was this translation helpful? Give feedback.
-
I took some time to create the spec for this feature here, if that helps |
Beta Was this translation helpful? Give feedback.
-
This feature is not championed by any member of the LDM. Until that happens, there will be no progress here. |
Beta Was this translation helpful? Give feedback.
-
There's a use case I've come across a few times now that's not mentioned here: needing to use Since pattern matching was introduced to the language and keeps improving I find myself using them more and more, but a number of times I've found myself having to write this annoying kludge: var @break = false;
foreach (var c in input)
{
switch (c)
{
case 'a':
// snip
break;
case 'b':
// snip
break;
case > '0' and < '9':
// snip
break;
case 'c':
// What I really want to do here is break out of the loop, not the switch statement
if (anyDigits)
{
@break = true;
}
break;
default: @break = true; break;
}
// Now I have to check my boolean to break myself
if (@break) break;
} It's possible this particular usecase could get its own feature, such as |
Beta Was this translation helpful? Give feedback.
-
Currently the cleanest way to break out of a nested loop is this:
If all we wanted to do was break out of the loops, we have to do
outer: {}
, which is sort've clunky. What we could do instead is use labeled loops like Java, so we can do this:And instead of:
We can just do:
The alternative is cleaner, more intuitive, and simpler.
I saw another post where they suggest doing
break 1;
to break the first loop, but that doesn't refactor well if the number of loops changes, you have to carefully make sure the numbers are correct.Beta Was this translation helpful? Give feedback.
All reactions