-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Collection expressions: implicit collections in spreads and foreach #7534
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
Conversation
* `E` is a *collection expression* and for each element `Eᵢ` there is an implicit conversion to `T`. | ||
* `E` is a [*conditional expression*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/expressions.md#1115-conditional-operator) `b ? x : y` where one or both of `x` and `y` is a *collection expression* and for each of the operands `x` and `y` one of the following hold: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would there be a way to obtain this behavior for conditional expressions without mentioning conditional expressions, so that it "just works" for switch expressions and other constructs? Maybe even covers nested collection expressions too?
For instance maybe piggy-backing off how target typing works through these expressions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be great :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I missed your reply to Cyrus in Teams:
It doesn't seem unreasonable to me to explicitly list the set of constructs this works with. It'll be a short list. And for any new constructs in the future, we'll probably want to explicitly decide to support this or not.
Fair. I want to start brainstorming now. :D Switch expressions are already potentially interesting to me.
What about generic method invocations? Could we avoid inferring <int>
on Iif
here? (Or will it not matter given the semantic lowering passing (int)43
to an Add(long)
call?)
long[] x = [42, ..Iif(cond, [43], [])];
ROS<T> Iif<T>(bool, ROS<T>, ROS<T>) => ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: i would not expect the IIf case to work. Specifically, because i wouldn't think this would work either:
ROS<int> r = IIf(cond, [1, 2, 3], []);
We don't have type inference flow in from return types inwards. As such, i wouldn't expect anything in the language would help with ths sort of case. If, of course, we ended up supporting htat sort of outerwards-in version of type inference, then it would be nice if this just got picked up.
|
||
* For each element `Eᵢ` in the collection expression there is an implicit conversion to `T`. | ||
|
||
An error is reported if the `foreach` iteration variable is *implicitly typed*. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rephrasing @CyrusNajmabadi's question here: Can we make this work with var b in
, without invoking natural typing, the same way Foreach<T>(ROS<T>)
would be called without natural typing?
- And maybe, permitting ref structs to be used?
foreach (var x in [refStruct1, refStruct2])
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be great too :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- And maybe, permitting ref structs to be used?
foreach (var x in [refStruct1, refStruct2])
Is it possible to also allow ref struct
in tuple being deconstructed ?
(Prop1, Prop2) = (new RefStruct(), new RefStruct())
sharplab.io
I know it's out of scope for this subject, but your question reminded me that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make this work with var b in, without invoking natural typing, the same way Foreach(ROS) would be called without natural typing?
We could use the best common type of the elements to determine the collection expression element type, which matches how the element type is determined for implicitly typed array creation expressions.
* `E` is a *collection expression* and for each element `Eᵢ` there is an implicit conversion to `T`. | ||
* `E` is a [*conditional expression*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/expressions.md#1115-conditional-operator) `b ? x : y` where one or both of `x` and `y` is a *collection expression* and for each of the operands `x` and `y` one of the following hold: | ||
* The operand is a collection expression and for each element `Eᵢ` there is an implicit conversion to `T`. | ||
* The operand has an [*iteration type*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/statements.md#1295-the-foreach-statement) `Tₑ` and there is an implicit conversion from `Tₑ` to `T`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so, one thing i don't like about this formalization is:
- it special cases condtional-expressions, but not something like switch-expressions.
- it's non-recursive. If you had:
byte[] y = [.. a ? b ? [1] : [2] : [3]];
, then i would expect that to work as well.
Basically, my preference would be that instead of special casing only the exact pattern of a ? b : c
, we write such that this is more generalized.
if (b) list.Add(z); | ||
``` | ||
|
||
*Should `switch` expressions be supported in spread elements?* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A strong correspondence in my mind is that a ? b : c
is equivalent to s switch { true => b, false => c, }
. Every time that is not true makes me sad :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. I think switch expressions in many ways supercede the conditional operator, so I've practically stopped using the conditional operator unless a) it's comparing non-constants, or b) it's so short that using it inline is reasonable.
foreach (bool? b in [false, true, null]) { ... } | ||
``` | ||
|
||
A *collection expression* may be used as the collection in a `foreach` statement. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similar to the above, this feels like we're overly special casing. What about:
foreach (int i in randBool() ? [1, 2, 3] : [4, 5, 6])
Basically, for both the spread case and the foreach case, come up with some rules that allow for these nested collections in a uniform one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, does this work:
foreach (int b in [1, 2, .. c ? [3] : []]) { ... }
basically, i'd like anything that we do with spreads to work uniformly in all these locations.
Closing, replaced by #7864. |
No description provided.