Roslyn: get values from loop

Created on 23 Sep 2016  路  17Comments  路  Source: dotnet/roslyn

Edit: Remove feature duplicated with one of pattern matching

I think something like pattern matching that could set value with switch should be able to do with for while foreach loop too

But instead of return single value. It could be continue to return many values

``` C#
//// Normally
var ints = new List();
for(var i = 0; i < 100, i++)
ints.Add(Random());

//// Linq
var ints = Enumerable.Range(0,100).Select((i) => Random()).ToList();

//// My proposed
IEnumerable iEints = for(var i = 0; i < 100, i++) {
continue Random();
}; // continue with value need both curly and semicolon

var ints = iEints.ToList();

//// Actually can write like this
var ints = for(var i = 0; i < 100, i++) {
continue Random();
}.ToList();
```

This syntax actually create IEnumerable<T> by fallback from all values at continue
The advantage of loop continue like this over linq is that it still live under same stack. So it could access ref variable in the function. And it clearly not create function and delegate

Also it could do select and where at the same time. If we just continue with empty then it will be like it filtered out

Area-Language Design

Most helpful comment

Your example code:

bool b = switch(someNumber) {
    case 0:
    case 1:
    case 2:
        break true; // return true out of switch
    default:
        break false; // return false out of switch
}

will be possible when pattern matching is fully implemented (hopefully in C# 7+1):

var b = someNumber switch (
    case x when x >= 0 && x <= 2: true
    default: false
); 

I assume you are offering a simplified example, as your specific example can already be written as:

var b = someNumber >= 0 && someNumber <= 2;

As for your loop example:

IEnumerable<int> iEints = for(var i = 0; i < 100, i++) {
    continue Random();
};

is already possible using yield:

IEnumerable<int> OneHundredRandomNumbers() => for(var i = 0; i < 100, i++) yield Random();

All 17 comments

ps. Another way instead of continue loop is #11882

@dsaf I don't think it related. Even with pattern matching it could still break normally

Your example code:

bool b = switch(someNumber) {
    case 0:
    case 1:
    case 2:
        break true; // return true out of switch
    default:
        break false; // return false out of switch
}

will be possible when pattern matching is fully implemented (hopefully in C# 7+1):

var b = someNumber switch (
    case x when x >= 0 && x <= 2: true
    default: false
); 

I assume you are offering a simplified example, as your specific example can already be written as:

var b = someNumber >= 0 && someNumber <= 2;

As for your loop example:

IEnumerable<int> iEints = for(var i = 0; i < 100, i++) {
    continue Random();
};

is already possible using yield:

IEnumerable<int> OneHundredRandomNumbers() => for(var i = 0; i < 100, i++) yield Random();

@DavidArno Yes that is just simplified example. The actual could be any complicate switch/case

There are many, such as switch string and enum And also this syntax could be written in complicate code inside switch too

``` C#
var value = switch(someCase) {
case "A":
if(someThing)
break "A and Something";
if(someOtherThing)
break "A and SomeOtherThing";

   break "A and Nothing";

case "B":
    if(someThingUnlikeA)
        break "B and someThingUnlikeA";

   break "B and Nothing";

}
```

Code like this is normal logic. So instead of copy variable to set in each place and break it after that like to make code longer. I suggest that we could do shorthand like this.

@DavidArno And for the loop proposal. I think you miss the point. I already mention using linq and it is the same problem. Your yield function make stack allocation and also cannot use ref arguments of function

You can try making ref argument like this

``` C#
void A(ref int i)
{
IEnumerable OneHundredRandomNumbers() => {
while(i < 100) // compile error here
{
yield Random();
i++l;
}
}

OneHundredRandomNumbers().ToList();

}
```

That's covered by the proposed pattern matching logic too:

var value = someCase switch (
    case "A" when someThing: "A and Something",
    case "A" when someOtherThing: "A and SomeOtherThing",
    case "A": "A and Nothing",
    case "B" when someThingUnlikeA: "B and someThingUnlikeA",
    case "B": "B and Nothing",
    ...
);

@DavidArno Oh sorry I never know that pattern matching already include this feature

I only know that pattern matching will relate to pattern in case block

Thank you very much

@Thaina.

Your yield function make stack allocation and also cannot use ref arguments of function

You are right, I missed the bit about ref. I can confidently say that I've never used ref, ever (save for possibly, many years ago, when interfacing to low-level C code), so I just don't get your point. Don't use ref; problem solved. :grinning:

@DavidArno There could be large struct that we don't want to copy and that would be benefit to use ref. And you also underestimate the problem of function stack allocation

And I try to avoid that by reusing native loop functionality

If your loop construct is in any way deferred then you wouldn't be able to use ref parameters anyway as the compiler would have to construct the same state machine as it would an iterator. If it's not deferred then there's no reason to have a new syntax to describe it when you can just use the loop to populate an array or collection.

@HaloFour Well.. I'm not so sure could it possible to defer. But I think it won't. I just think there could be some in stack collection that would be the better data structure than list. Things that could enumerate and compatible with linq

As I state, the thing I would like to improve are avoiding function stack so it would be inline logic and possibility to use ref variable to populate collections

At least it would be syntactic sugar for linq style. The same reason that you can use switch syntax to declare variable instead of declare variable and set inside switch

there are new syntax like this

``` C#
let area = primitive switch (
case Line l: 0,
case Rectangle r: r.Width * r.Height,
case Circle c: Math.PI * c.Radius * c.Radius,
case *: throw new ApplicationException()
)

I would like to have something like this but as a loop. Maybe like this

``` C#
var areas = foreach(var primitive in primitives) {
    continue primitive switch (
        case Line l: 0,
        case Rectangle r: r.Width * r.Height,
        case Circle c: Math.PI * c.Radius * c.Radius,
        case *: throw new ApplicationException()
    );
}

I don't see that as any improvement over the existing loop syntax for populating a list:

var areas = new List<int>();
foreach (var primitive in primitives) {
   areas.Add(primitive switch (
        case Line l: 0,
        case Rectangle r: r.Width * r.Height,
        case Circle c: Math.PI * c.Radius * c.Radius,
        case *: throw new ApplicationException()
    ));
}

If anything it's worse because it binds the compiler to a specific implementation of a collection, at least if you want support for var.

I want to say that I've already seen a proposal for making keywords like if and foreach produce return values but I haven't been able to find it. In my opinion it's not C#-like to have those keywords produce values.

@HaloFour As I said. same reason with that new syntax switch
Think about this

``` C#
float area;
switch (primitive)
{
case Line l: area = 0; break;
case Rectangle r: area = r.Width * r.Height; break;
case Circle c: area = Math.PI * c.Radius * c.Radius; break;
case *: throw new ApplicationException()
}

Now what difference? It can work normally with this syntax so why they have that new syntax?

The need to reference a variable is the problem. It could and should be shorten. It also about type of object that could be return. It might be list but list of what? That could be other type not just int such as

``` C#
var list = new List<SomeLongNameOfBaseType>();
foreach(int i in numberList)
{
    if(i == 0)
    {
        var obj0 = new DerivedTypeForCase0();
        // Do something for case 0
        list.Add(obj0);
        continue;
    }

    if(i % 2 == 1)
    {
        var obj1 = new DerivedTypeForCaseOdd();
        // Do something for case odd
        list.Add(obj1);
        continue;
    }

    var obj2 = new DerivedTypeForCaseEven();
    // Do something for case even
    list.Add(obj2);
}

So I think this better

``` C#
var list = foreach(int i in numberList) {
if(i == 0)
{
var obj0 = new DerivedTypeForCase0();
// Do something for case 0
continue obj0;
}

if(i % 2 == 1)
{
    var obj1 = new DerivedTypeForCaseOdd();
    // Do something for case odd
    continue obj1;
}

var obj2 = new DerivedTypeForCaseEven();
// Do something for case even
continue obj2;

};
```

@Thaina

An expression form of switch is common in functional languages for pattern matching. You're right that the syntax is largely unnecessary as the same functionality can be accomplished through nested if/else or ternary operators. Although a single switch can optimize better by reusing patterns.

Functional languages also often have list/query comprehension, but C# already has that and it's called LINQ. I see no reason to duplicate it yet again. I disagree that you're syntax is sufficiently "better" than all of the current existing forms.

Here's the issue that discussed making existing keywords behave as expressions: #3640

@HaloFour And as I was mention about #11882 that if linq could force function to be inline and then let we could use ref argument in it. Then I would be satisfied

And I don't think that new switch syntax is unnecessary. I think it better than current switch and I want that for such times. It better to the point that I want it for looping things too

But yeah, I have look at #3640 and seem like it a bit alike, except I want to make it be inline and can use ref. So maybe tag a duplicated? Is the inline implementation distinct enough?

Closing as I believe this conversation is out of date. If you want to continue on this topic, please create a new thread in csharplang.

Was this page helpful?
0 / 5 - 0 ratings