Skip to content

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

Closed
wants to merge 6 commits into from

Conversation

cston
Copy link
Contributor

@cston cston commented Sep 13, 2023

No description provided.

Comment on lines +270 to +271
* `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:
Copy link
Contributor

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?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be great :)

Copy link
Contributor

@jnm2 jnm2 Sep 15, 2023

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>) => ...

Copy link
Member

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*.
Copy link
Contributor

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])

Copy link
Member

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 :)

Copy link

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

Copy link
Contributor Author

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`.
Copy link
Member

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:

  1. it special cases condtional-expressions, but not something like switch-expressions.
  2. 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?*
Copy link
Member

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 :)

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.
Copy link
Member

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.

Copy link
Member

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.

@cston
Copy link
Contributor Author

cston commented Feb 4, 2024

Closing, replaced by #7864.

@cston cston closed this Feb 4, 2024
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

Successfully merging this pull request may close these issues.

5 participants