-
Notifications
You must be signed in to change notification settings - Fork 1k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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
C# Feature Request: Allow value tuple deconstruction with default keyword #1358
Comments
That's because this is a deconstruction, and you need a type on the right to try to find the appropriate .Deconstruct method. What you can do is |
But the type for .Deconstruct method is known by types of |
No. '.Deconstruct' is called on a type. i.e. you have a type "class Frob". There can be a .Deconstruct method for it like so:
Then, if you can have: Frob f = default;
(int x, bool y) = f; And this is shorthand for: Frob f = default;
f.Deconstruct(out int x, out bool y); In the example you have, it would be equivalent to having the following: default.Deconstruct(out int x, out bool y); But you can't call .Deconstruct on 'default' because it has no actual type. |
There are no (int x, bool y) = default; to: (int x, bool y) = (default, default); (which is valid C# 7.1 syntax) and thus on to whatever other lowering it already does. |
I thought I already asked for this but I guess I've just brought it up in the chat. It would be practically useful, but besides that, it's consistent with the concept of treating operations on syntatic tuples as distributed to each individual syntactic element. These all make sense and the first two, especially the second, would have been nicer than the (int x, bool y) = default;
(x, y) = default;
(x, string z) = default; |
But there is no tuple here, only deconstruction. A tuple isn't the only thing that could possibly be deconstructed. And do you want the compiler to create a tuple just to then immediately deconstruct it? |
I just don't see the use case for this. You don't want to create a tuple here (otherwise you would use one) and you don't need to deconstruct anything either. You just want a fancy way to initialize two variables on one line. And if you really feel like you need to do that (even though there's no point in doing that), you can always do |
Yes there is. You use deconstruction to deconstruct a value into multiple variables. There's literally nothing to deconstruct here. Therefore not even any need to use deconstruction. |
@Neme12 There is no ValueTuple, but there is a syntactic tuple comprising
Reducing the boilerplate in an annoying scenario.
It's equally clear both ways. |
|
It looks very clear and intuitive to me. Given everyone's reaction to it now and when I first brought it up, I'm not convinced it's actually that confusing. |
I don't see either one of: int x = default;
bool y = default; or if you want (int x, bool y) = (default, default); as being "the boilerplate in an annoying scenario". And besides, you might later want to change one of those to not being |
I will try to describe it from my perspective of "language end user": Tuples allow multiple data transfers, or variable assignments, at the same time. One - from my perspective - quite popular scenario for this expression might be some kind of
You never will need to set any |
@Neme12 For me it was more than two lines, or repetitions of |
@KnorxThieus Thanks for the Try/out example. That's really the best use case for default because when returning false it really says: "put in the default value, I don't care what it is and the consumer of this API shouldn't care either if I return false" as opposed to "I know exactly what kind of value I want but I just write default because it's shorter and I know the value I want happens to be the default one." |
There is already precedence here. The compiler already supports (a, b, c) = (d, e, f); as a "fancy" way of assigning multiple variables on one line and the compiler has been optimised to remove the tuple and deconstructs from the resultant IL. And @jcouv has already suggested that the same could be applied to tuple equality, so that: (a, b, c) == (d, e, f) again optimises away the tuples from the resultant IL. So whilst, var (a, b, c) = default; is indeed just a fancy way to initialize multiple variables, such fancy features add that touch of class to the language in my view. |
I know. I've used that myself on occasion. But it's a little different because on the right you unambiguously have a tuple. But there's no tuple target type in that spot so
😃 ...I guess I can't argue with that. |
Just to be clear: special-casing is what we are asking for. Just like |
duplicate of #583? |
There is no ValueTuple int eh example at all. There is a deconstructed variable on the left, and a 'default' expression on the right. The way decosntructed variables work is by figuring out how to deconstrct the value on the right. For ValueTuples there is a known way for how to do that. But there is no ValueTuple here. So the question is: what happens with 'default'? |
Note: i am not arguing against allowing this simplified form. It seems fine to me. I'm just explaining that the reason this doesn't work today is correct, and by design as per how tuples, deconstruction and 'default' all work. |
I do not know about factic implementation of the
So to me, it seems absolutely logical implementing this keyword also for deconstruction. |
@alrz Confirm the duplicate. How can I mark this on GitHub? |
@KnorxThieus What makes this case different is that there's no 'type_of_expected_expression' after a deconstruction. After all, you could have your own type that is deconstructable, it doesn't have to be a tuple. |
@Neme12 Isn't it |
In thsi case you don't have a type, you have a declaration of two variables. The distinction is subtle: (int x, int y) t = ... Here you have a variable called t. it is a tuple. it has members 'x' and 'y' that are both ints. This is different from: (int x, int y) = Here you have two variables, x and y. They both have type int. There is no type on the left side. -- This is important to recognize this because of how 'default' works. 'default' works in contexts where there is a contextual type that default can be 'coerced' to. So, using the above two examples, this would be fine: (int x, int y) t = default; This is fine because there is actually the tuple type that default can be coerced to. And it is equivalent to you writing out: (int x, int y) t = default((int x, int y)); -- Now, that's not to say that what you're asking for is unreasonable or impossible. But it means adding a special case into the language. Specifically, if you have a deconstruction assignment, and you have 'default' on the right side, then infer that that default should effectively be compiled as if you wrote: (int x, int y) = (default, default); Note: it's important that it be treated that way, and not treated as: (int x, int y) = default((int y, int y)); The reason for that is that the deconstruction does not have to look like this. it could also look like: int x, y;
(x, y) = default; You want this to translate to: int x, y;
(x, y) = (default, default); // legal, sensible. Not: int x, y;
(x, y) = default((x, y)); // illegal, non-sensible. |
That's a reasonable interpretation. As you have importantly deduced though, you must have a defined "type_of_expected_expression".
Yup. int.Parse takes a string, so type_of_expected_expression is 'string'.
Yup. With an assignment you can use the type on the left to determine things. However, it's important to realize you must have an actual entity on the left that has a type. A deconstruction has no type. i.e. when i write: int x, y;
(x, y) = ... Then "(x, y)" is not an actual value. It cannot be referred to. It has no type. It is just a way of saying "i want to refer to these n locations for the assignments to go into. A good way to think about this is how this is rewritten. When you write the above, what you actually get is: int x, int y;
....Deconstruct(out x, out y); Where is there any 'type' here except for the types of 'x' and 'y'?
I disagree that it's logical. But i can see the desire for it. It would effectively be saying that the language should have special treatment for 'default' in a context where there is a deconstruction, but there is no type to actually mean "assign the default value to everything on the left". Note that this does not fit your original intuition. i.e. "but I learnt it as some kind of compiler-generated abbreviation of default(type_of_expected_expression)." In this case, it would not be the same as "default(type_of_expected_expression)". Instead, it would translated to: int x, y;
(x, y) = default; becomes int x, y;
x = default;
y = default; -- Now, i personally i'm on the fence as to why this would actually be valuable to have. It's basically saying: i want to declare 'n' variables up front, but give them all the default value. I don't know if that's a pattern that's worth making super succinct. You can already just write: int x = 0, y = 0; So why is it actually better to write out: (int x, int y) = default; It just seems more verbose and 'cutesy' rather than actually an improvement over the existing ways to do things. |
I wanted to use it to satisfy assignment rules for certain code paths. I think there were three out variables in one parsing scenario, and early returns became very painful. |
Opened championed issue #1394 and implemented the change (dotnet/roslyn#25562). I'll raise with LDM to see if we could take this change for C# 7.3. |
That change didn't make it into C# 7.3, but is ready to merge, so should be in C# 8.0. |
We haven't approved it for this release. Perhaps a future one. |
I was a little suprised that this didn't work in 9.0, realized deconstruction doesn't do target-typing yet. (object i, int j) = b ? (new a(), 1) : (new b(), 2); // error |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Hi,
I apologize for neglecting the form, but I see not how to fill "expected behavior" etc. correctly.
In C# 7, we currently are able to use the
default
keyword in the following way:int x = default;
meaning
int x = default(int);
We also can use tuple deconstruction like that:
(int x, bool y) = (42, true);
Or with the default keyword:
(int x, bool y) = default((int, bool))
But what seems to be impossible to me at the moment is this pattern of syntax:
(int x, bool y) = default;
In my eyes, the is some kind of logical gap that deserves to be filled soon. Okay, in this example, we could either write
var (x, y) = default((int, bool))
But assume
x
andy
have been declared already before, then the use ofvar
keyword is not possible.That's why I would like to propose allowing this syntax.
While enjoying this language for a few years now, I am new to this forum, so I would be thankful to someone explaining me what's happening next with this proposal - if not already submitted by another user, but I couldn't find one. Thank your for your attention!
Design Meetings
https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-09-26.md#ungrouped
The text was updated successfully, but these errors were encountered: