Shorthand for common for loop formats #2810
Replies: 132 comments 11 replies
-
I don't see the need for that. If you want less typing then you could use code snippted for almost all of your cases, which would end up with an equal amount of key strokes. If your concern is readability, i think it increases the readability, if there are fewer options to express the name loop. And regading |
Beta Was this translation helpful? Give feedback.
-
I generally really like this with the exception of the reverse "forr". It seems very typo-prone. |
Beta Was this translation helpful? Give feedback.
-
rfor instead? |
Beta Was this translation helpful? Give feedback.
-
I don't think it's worthwhile adding a bunch of different variations of C# may gain the ability to loop over a range in the future, which would cover what you've listed as the common use case: foreach (var i in 0..50) { ... } |
Beta Was this translation helpful? Give feedback.
-
Until then, you can still use: foreach (var i in Enumerable.Range(0, 50)) { ... } |
Beta Was this translation helpful? Give feedback.
-
I don't understand how snippets are relevant for this discussion. You can use snippets to reduce keystrokes for anything but it doesn't mean that that thing is good or bad or that it can't be improved upon. |
Beta Was this translation helpful? Give feedback.
-
Because snippets can already decrease the amount of typing required to implement a feature and require infinitely less resources to implement than a language feature. You can have the snippet today. Language features can take years, even if the LDM champions it. |
Beta Was this translation helpful? Give feedback.
-
Snippets and auto-complete tools are certainly wonderful to have! I use them a ton myself for for loops. However, this proposal doesn't have almost anything to do with reducing typing (even though that is a nice side-effect), it's about improving clarity and readability by displaying what is relevant without much clutter and boilerplate around it, in order for the person reading to more easily see what the piece of code is actually doing |
Beta Was this translation helpful? Give feedback.
-
Then i don't get why do you want theese new loops. You complaining about boilerplate code, which in my opionon isn't a big deal. But your proposed syntax can be very likely missinterpreted as an function call, by a human reader. If you didn't want to reduce keystrokes, then you are right, that snippets aren't related to the problem. My understanding was you wanted reducing the boilerplate code you write. |
Beta Was this translation helpful? Give feedback.
-
I disagree that the proposed syntax is any clearer or reads any easier than existing // proposed
for (i, 20)
// today
for (var i = 0; i < 20; i++)
// with ranges (future, potentially)
foreach (var i in 0..20)
// proposed
for( i, myArray )
// today
foreach (var i in myArray) |
Beta Was this translation helpful? Give feedback.
-
As I mentioned in the proposal itself, it doesn't have to use a comma, it might as well use a semicolon in case it might be mistaken for a function call. But if you don't think boilerplate code and largely useless symbols that clutter your code is a problem, then I guess you wouldn't find this very useful, sure. Also, I'm not "complaining", that's pretty hecking unnecessary of you to levy at me :/ |
Beta Was this translation helpful? Give feedback.
-
I don't agree. It's more about the readability of: The first is shorter to type but also (imo) cleaner and easier to read. Less keystrokes are just a side effect.
I don't think anyone is going to mistake I do, however, think the |
Beta Was this translation helpful? Give feedback.
-
I emphasized you, because i ment the code you write as a person, in comparison to the code a tool or a snipped would insert for you. It's nothing about levy levying! I simply don't agree an the neccessarity of your propoal. |
Beta Was this translation helpful? Give feedback.
-
Ranges certainly make things a bit better! I hope they're good on not generating garbage and such for constants! As for the second one:
As I mentioned in the original post, I'd be fine keeping the type declaration too, I'd just prefer not to have to do it, although I think you might be misunderstanding proposal 2, I'll update the post to clarify! The proposed As in, the i would be an index, not an iterator for elements in the array |
Beta Was this translation helpful? Give feedback.
-
It's quite terse and completely hides the fact that a variable declaration is involved (which immediately makes it very inconsistent with the rest of the language). It implies a range without actually specifying a range. What do you do if you need to start from 1? -1? A variable? Now you need another flavor of that loop. Range syntax is already going to ship as a language feature in C# 8.0 in about a week. IMO it makes more sense to base a loop around that syntax, probably by making them enumerable but having the compiler optimize to a simple for loop. |
Beta Was this translation helpful? Give feedback.
-
Yeah, thank you very much for sharing this data @CyrusNajmabadi. |
Beta Was this translation helpful? Give feedback.
-
Fascinating new result. I can beat out the
Full code is: [Benchmark]
public int Indirect()
{
int val = 0;
foreach (ref var v in new IndicesEnumerable(source))
{
v *= 2;
}
return val;
}
[Benchmark]
public int Direct()
{
int val = 0;
for (int i = 0; i < source.Length; i++)
{
source[i] *= 2;
}
return val;
}
public struct IndicesEnumerable
{
private readonly int[] _source;
public IndicesEnumerable(int[] source)
=> _source = source;
public IndicesEnumerator GetEnumerator()
=> new IndicesEnumerator(_source);
}
public struct IndicesEnumerator
{
private readonly int[] _source;
private int index;
public IndicesEnumerator(int[] source)
{
_source = source;
index = -1;
}
public bool MoveNext()
=> ++index < _source.Length;
public ref int Current
=> ref _source[index];
} No need to double index looks beneficial. Admittedly, only valuable if you're reading/writing to the same location. But there does seem to be some benefit in that case. Core loops are now:
So the indirect approach is genuinely less instructions here. Go .net core + |
Beta Was this translation helpful? Give feedback.
-
FWIW, Halo brings tremendous insight. He may not be speaking from direct experience in the internal process/discussions, but he has certainly absorbed a ton of knowledge about what's led to here, including all the reasoning and arguments that have driven the relevant parties. I think he speaks quite well to very common perspectives and attitudes in these spaces. After all, if he didn't, you'd see me contradicting him all the time ;-) |
Beta Was this translation helpful? Give feedback.
-
Okay @CyrusNajmabadi, so now do this. 😆 for (var i = 0; i < arr.Length; i++)
{
ref var v = ref arr[i];
v *= 2;
} At this point of silly optimisation we really shouldn't even be using arrays, it's unsafe pointer time. Presumably that puts It's interesting that this isn't done automatically for something like |
Beta Was this translation helpful? Give feedback.
-
No clue. Feel free to raise issue with .net core and the jitter :) |
Beta Was this translation helpful? Give feedback.
-
Oh, i'm pretty sure it would be hte same :D This was more about how you could avoid indices and have foreach even be faster! It's an 'aside' though, and not relevant to the main discussion. Just something nifty i thought i'd share :D |
Beta Was this translation helpful? Give feedback.
-
Yeah I was kidding. We should probably stop dragging this issue off track... |
Beta Was this translation helpful? Give feedback.
-
I think it would be great if someone with a vested interest could gather all of the different ways of looping that people have brought up in this thread (indices, indices + element, ref element, range, reverse range, etc) and put them in a NuGet package. That way the people looking for improved ways of doing these sorts of loops can standardise on an API. I think Cyrus has made an incredibly robust point that practically all of the requested looping syntax additions can be made at a library, rather than language, level, with very small (sometimes negative) performance costs. I think there's still a background feeling among some (many?) people that Range should be enumerable, but I think that's perhaps a separate (corefx?) issue. |
Beta Was this translation helpful? Give feedback.
-
Extension GetEnumerator has now been merged into the master branch of Roslyn, and should be coming in C# 9.0. With it you can now foreach over a range using extension
Once support is available in VS for this preview language feature you can install this nuget package https://www.nuget.org/packages/RangeForeach/0.0.1 to get an extension GetEnumerator method for Range. Or you can just copy the file straight from https://github.com/YairHalberstadt/RangeForeach/blob/master/RangeForeach/RangeExtensions.cs. |
Beta Was this translation helpful? Give feedback.
-
Closing out as this syntax is actually legal. And, as @YairHalberstadt, can already have legal semantics. |
Beta Was this translation helpful? Give feedback.
-
@CyrusNajmabadi I don't believe the original syntax suggested by the OP is legal. |
Beta Was this translation helpful? Give feedback.
-
OP's original suggestion hasn't been implemented, but extension enumerator does get us closer to that design goal with some additional methods (extension or otherwise), e.g. for (var i in 20.Times())
{
// ...
} |
Beta Was this translation helpful? Give feedback.
-
C# 9.0 is out now although it doesn't seem like they added support for enumerating ranges, so you can't use it in for loops or foreach loops 😭 Hopefully they'll think of adding it just to the language itself without a need for work arounds |
Beta Was this translation helpful? Give feedback.
-
While we're at it, I'd like to have for loops where the loop variable is treated like a constant within the scope of the loop body, like it is already the case with foreach loops. And (while we're at it, too), what about foreach loops with an int index besides the loop variable? I mean, syntactic sugar for OTOH, this could be done with an expanded iterator: |
Beta Was this translation helpful? Give feedback.
-
I like the concept! If implemented, I'll leave it to the syntax experts to decide the syntax. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I'd like to propose shorthands for the most common for loops we use!
It's one of the few parts of C# that makes it feel like a dated language, which is such a shame given how modern and wonderful it feels otherwise 💖
Observed Problem
It seems to me like for loops are, in the vast majority of cases, 90% boilerplate
For some actual data on this, I ran some numbers on Budget Cuts, a released video game project at work (multiple platforms, written in C#).
When I write a for loop, the only two things I usually want to specify is variable name and iteration count. The solution should allow you to specify those two, without the rest of what is so often boilerplate
Proposed Solutions
for( i, 20 )
Short for loops with only a local variable name and iteration count. This is the one that would massively reduce boilerplate in all those 81% of cases. This should be considered the core of this proposal, what follows are more optional but quality of life variants I'd love to have!for( i, myArray )
Loops that can iterate for the number of elements given an array, list or any other enumerable type. Note that i here would be the index, just like for loops, unlikeforeach( var i in myArray )
for( 20 )
/for( myArray )
Loops without a local variable. This one is the least important, since this is often quite rare, but I feel like this would be neat for completeness. It would also be useful educationally when introducing the concept of loops to people who are new to programming, where the concept of a local variable might seem confusing at firstforr( i, 20 )
/forr( i, myArray )
/forr( 20 )
Built-in reverse for loop versions of all of these, to avoid the unreadability and risk of off-by-one errors when manually writing themNotes on Syntax
The separator symbol could be either comma
for( i, 20 )
or semicolonfor( i; 20 )
. I personally prefer the former, as it distinguishes itself from the regular for loop syntax, while remaining familiar enough to look like a list of two arguments. But, it could go either way as far as I'm concerned. I can see why you'd want to prefer the latter, which I'd say is still better than not having this feature at all!We might also want to still have the explicit type for the variable like
for( int i; 20 )
to make it look more C#-like. I personally would prefer that the type would be optional - that it would implicitly useint
if the type is not specifiedWe might want to use a different term for these in case we want to distinguish them from the regular for loop. We could use terms like
loop
orrepeat
Likewise, the syntax for reversing the loop might also look different. I prefer
forr
, since it's short and easy to write, plus, it matches the reverse loop autocomplete in Visual Studio (and perhaps in other IDEs as well, I haven't checked!), but, other ways are fine too, as long as it doesn't involve manual indexing and offsetting stuff by oneThanks for giving this a read!
Beta Was this translation helpful? Give feedback.
All reactions