-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Proposal: Allow using discard for throwaway variables #8074
Comments
I like the idea of being able to use wildcards to discard identifier names in various circumstances but this particular use case seems fairly contrived and extremely narrow. Building a language feature around the design of a specific unit test library does not sound like a good idea, and frankly you have 53 single-character temporary variable names at your quick disposal. |
#6400: |
What about my second example - indicating that a pure function result is not used?
Those 53 single-character names can also be used for "omitting" lambda parameters. After all if a lambda parameter is often being omitted, surely it indicates a problem with specific API? |
I'm not sure that the language should specifically cater to overzealous analyzers either. Between Resharper and FxCop I'd probably still be trying to figure out how to compile HelloWorld.cs.
Fair point, and running through the alphabet isn't all that uncommon today.
I'd say that it's not that uncommon for a delegate to accept more parameters than is always required. But invoking a property getter and immediately discarding the result? In my opinion there is no valid situation in where that makes sense in an API. |
Fair enough. |
I think this request has been prematurely closed. F# has this feature (it uses |
I believe wildcards will be supported in patterns. |
As well as being of use in patterns, the other main use for this feature in F# is for handling functions that return a tuple, triple etc, when the calling code is not interested in all values. Using the tuple syntax described in Early View of C# 7 with Mads Torgersen, we may have a method declared as:
Then, if I only care about
Instead, using a throw-away variable, we get a much cleaner piece of code:
or
Anywhere you'd use |
that's exactly what #6400 is about. |
And #20, for lambdas. button.Click += (*, *) => { Console.WriteLine("Clicked!"); }; I think there is one for bool valid = int.TryParse(s, out *); To note I like the idea of wildcards in many scenarios, I'm just not terribly keen on them for throwaway locals which is what this proposal is specifically about: var * = SomeFunction(); |
#6400 is a hugely mixed bag and it's hard to see what it's actually supposed to be about. The outcome (as documented in the pattern spec) seems to the ideas of It's very unclear therefore (at least to me!) how this covers the idea of throw-away variables. |
Allowing throw-away variables in some situations, but not others seems a recipe for a hugely confusing language extension, especially when the
being valid and
being invalid. Seems an odd decision to me. |
Per #6400 that is already the case, Also per @alrz 's comment let Point(var x, *) = point;
Console.WriteLine($"The X coordinate is {x}."); |
@DavidArno thrown-away variables are not about |
Whilst C# does indeed allow the following:
where
Now it's obvious that I'm just disinterested in the return. Due to the beauty of the Roslyn analyzers, I can also enforce this rule, without requiring a breaking change to the compiler. A further use of this syntax can be seen in my own Succinc<T> project (which provides a means of implementing pattern matching, options and other other union types and the like in the existing C# language). I make extensive use of
Here the line So I'd contend you are wrong: |
Do you have a link to a detailed explanation of Thanks. |
@DavidArno As I mentioned earlier I don't think language features should be designed around how testing libraries happen to function. You already have a perfectly valid form of syntax for accepting the result, and you can continue to use the legal identifier The proposal for In short, you're applying the pattern on the left-hand side of the assignment to the expression on the right. In this case it is taking the variable Not a terribly useful example but you can effectively use any complex pattern defined in the spec. To put it into context with the example that you provided: let (var x, *) = Compute(someValue); Which takes the tuple returned from |
I'm really not following the thinking behind your position here.
What I'm arguing for is consistency across the language for any new feature. So, for example, you suggest that
That begs the question, will I be able to declare an event handler like this:
when I don't use either parameter? I suspect the answer is "no", which will mean yet another inconsistency. Currently, my use of |
@DavidArno I've been proposed something like
The reason is that it'll be a breaking change?
Spec for patterns is not at its final stage and might be changed. Similarly, I argue that constant-pattern also should be a part of complex-pattern so that you could write |
There is something to be said for consistency, sure. However, sometimes it doesn't make sense to apply things in absolutes. For example, you can't use Should you be able to use Also, pertaining to your test case use case, Microsoft's Framework Design Guidelines specifically state to avoid throwing exceptions from property getters. |
How would allowing the return value of a method be assigned to the throw-away
If the method is an event handler (or any other delegate-fulfilling method), then it must declare a set of parameters, regardless of whether it uses them. Using You make a good point re
shows yet another worrying inconsistency creeping into the language. What's that
|
The fact that a method is used to subscribe to an event doesn't change anything else about that method. It is still a formally declared method and parameter names are a part of that. If you want shorthand, potentially with argument omitting, you have lamdba syntax, which works just fine with event handlers. The switch (Compute(someValue()) {
case (var x, *):
Console.WriteLine($"X is {x}");
break;
default:
Console.WriteLine("Oops, return value wasn't the tuple we expected.");
break;
} |
OK, leaving aside
I'd expect the following example to be valid, rather than your example, so there seems something wrong with the pattern matching spec if it's not:
|
I explained the purpose of You're very likely correct that my example would result in an "unreachable code" warning, I was keeping the sample simple. Your example would probably be a better demonstration of pattern matching in |
@DavidArno |
Could you show an example of what you mean please? |
@DavidArno int x = 5;
switch (Compute(someValue())
{
case (x, *):
Console.WriteLine("X is 5");
break;
} But after checking pattern matching spec once more I was surprised it is not allowed. class Test
{
const int x = 5;
void Method()
{
switch (Compute(someValue())
{
case (1, y):
Console.WriteLine($"X was 1, Y was {y}");
break;
case (x, *): // matches constant x = 5
Console.WriteLine("X wasn't 1");
break;
}
}
} |
I decided to go away and experiment with the
The important thing to note here is the line In other words, even pattern matching doesn't support the use of "throw away" variables in the manner that I'd suggest that |
For starters, the functionality of the compiler for the MVP summit was far from complete. Second, you were using type patterns, not record patterns. As such the proper syntax for omitting the variable is simply omitting the identifier, e.g. The switch statement will likely retain |
Thanks for your reply. However, the comment "For starters, the functionality of the compiler for the MVP summit was far from complete." really highlights the problem with trying to work out what on earth is going on with C# 7. That demo's write-up is the most up-to-date single demo of the language I can find. It was demonstrated at a significant event. Why wasn't the most up-to-date thinking shown there and where do I find what is the latest version? There's dozens of random issues, each with conflicting information with no one appearing to be responsible for maintaining a true record of what is going on. How do you expect those not in the "inner circle" to offer opinion in a meaningful way if every observation is met with a "your are using/reading the wrong version of the spec"? If the "switch expression" offers a different syntax to the switch statement, why was Why if, for a "type", can if use Where are these - apparently random, and contradictory - decisions documented? |
I understand your frustration, it's not always clear what the current thinking or design is for a proposal and the roadmap issues haven't been updated in forever. The MVP summit three months ago was probably the last time Microsoft did a big demonstration for an audience so they put extra effort into demonstrating the features and we haven't seen anything similar since. For the more active proposals, like pattern matching, the best glimpse any of us really get is the documents that they've been updating that are generally referenced in the specs. As for why The type pattern and record pattern solve very different problems. The type pattern just does a type check optionally assigning the cast type to the identifier so that you don't have to cast: object obj = new Person("Gates", "Bill");
if (obj is Person person) { }
// roughly equivalent to
if (obj is Person) {
Person person = (Person)person;
} Whereas the record pattern deconstructs the properties of record types and permits pattern matching against them: object obj = new Person("Gates", "Bill");
if (obj is Person("Gates", var firstName)) {
Console.WriteLine($"obj is a Person with the last name of Gates and the first name {name}");
} |
Bill Wagner talk on C# 7 at NDC London is now available at https://vimeo.com/154708153. It backs up what you say regarding Thanks for the clarification re type patterns and record patterns. |
I feel I've significantly hijacked this issue away from its original purpose (to provide I will therefore create a new issue to propose the latter change as a step to achieving the former. |
Latest developments on this issue- #14794 |
@dsaf, RC.2 includes a first crack at the discard feature. They are allowed in out vars, deconstruction, patterns and also in assignment. The latter is I'll go ahead and close this issue. Thanks for the proposal. |
Is RC2 released yet? I thought RC2 was the update to vs15 released a few days back, but |
@DavidArno Yes. I think RC.2 was released a week back. Can you share the specific version/build version of VS2017 you're using? Or even better, the git SHA of the csc it uses (from memory: look under program files folder, then Microsoft Visual Studio, then 2017, then Tools or MSBuild, then the properties on csc.exe). I checked the dev15-rc2 branch and it does have the test too. |
The Properties -> Details view shows the product version as "2.0.0-rc2-61205-04. Commit Hash: 743c...". Not sure how to get at the rest of the number though, sorry. Does that look like the right version? I'm just trying to add a test line of |
@DavidArno I have the same version as you do (csc.exe shows 2.0.0-rc2-61205-04. Commit Hash: 743ce3b..." and VS shows "Version 15.0.26006.2 D15REL") and |
@jcouv Thank you! |
My apologies: the problem existed between the chair and keyboard! It compiles fine; the error message I was seeing is understandably coming from R#, which hasn't caught up with this new feature yet. Sorry for wasting your time 😞 (and thanks for implementing this feature; really happy with it now I've worked out what I'm doing! 😁) |
Related to #20 (comment) and #8049.
Practical use cases:
http://nsubstitute.github.io/help/received-calls (Checking calls to properties)
https://confluence.jetbrains.com/display/ReSharper/Return+value+of+pure+method+is+not+used
Before:
After:
The text was updated successfully, but these errors were encountered: