dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
18.88k stars 4.01k forks source link

WITHDRAWN #14697

Closed ghost closed 7 years ago

ghost commented 7 years ago

We've decided to switch to C++/CLI.

alrz commented 7 years ago

variable i is already declared in this scope

Binding to existing variables probably needs a different syntax (#13400).

Note that this is what we need to do with out parameters. you need to declare variables beforehand. So it doesn't add much value to out var.

vladd commented 7 years ago

I don't think that the implicit variable declarations is a good thing. In particular, it helps to hide typos. Consider:

int i;
if (!(first is int i)) return;

int j;
if (!(second is int i)) return; // here is a typo

Now, the value of i is silently overwritten, no warning is issued. Having the variable declaration explicit would catch this problem.

svick commented 7 years ago

out parameters can't be fields or properties.

They cannot be properties, because there is no way how setting a ref or out parameter would call the property setter. But they can be fields.

DerpMcDerp commented 7 years ago

I like eliminating the implicit leaks by using the old style way of using local variable declarations to control scope.

I don't like 1. the typo prone unification magic of this proposal 2. specifying the type of a variable more than once.

A better proposal would be more akin to something like:

int a;
foo(out a);

// or maybe a different keyword than "out"
if (3 is out a) { stuff; }
vladd commented 7 years ago

@Kaelum Well, detecting typos is a primary reason why we need to declare variables at all. If we would consider guarding against typos not important, we could omit var altogether, and make all variable usages effectively "implicit declarations". Surely you don't want to propose this way, do you?

AdamSpeight2008 commented 7 years ago

The variable being introduce should be to outer scope be default

if( !(o is int i) )
{
 /* i is in scope and definitely not assigned */
 throw new ArgumentException("Not an int", nameof(o));
}
/* i is in scope and definitely assigned. */

and explicitly require a code to specify a change to the inner. For example by prefixing ~ on the variable identifier.

if (o is int ~i)
{
  /* i is scope and definitely assigned */
}
/* i is not in scope. /*
if( !(o is int ~i) )
{
  /* i is scope finitely not assigned */
}
else
{
 /* I is scope and definitely assigned */
}
/* i is not in scope. /*

i prior exists

int i;
...
if( !(o is int i) )
{
 /* i is in scope and definitely maybe assigned */
 throw new ArgumentException("Not an int", nameof(o));
}
/* i is in scope and definitely assigned. */
int i;
...
if (o is int ~i)
{
  /* i is scope and definitely assigned */
 /*  also is and error as i is be reused for a variable declaration */
}
/* i is in scope. /*
int i;
...
if (o isnot int ~i)
{
  /* i is scope  not assigned a new value*/
 /* also is an error as i is being reused for a variable declaration */
}
else
{
 /* i is scope and definitely assigned a value.*/
 /* also is an error as i is being reused for a variable declaration */
}
/* i is in scope. /*

if the type of i is incompatible with the one in the pattern, it is an error in all cases.

tamlin-mike commented 7 years ago

Actually, that spam made me think of something that possibly could solve this issue.

I'm totally against allowing scope escaping by default, but if someone really wanted it (for whatever reason), what if they could opt-in to that behavior?

I'm specifically thinking about an otherwise illegal token, where maybe ! could stand out?

Example:

if (o is int i) { /*' i' is live */ }
/* 'i' doesn't exist */
if (o is int !j) { /*' j' is live */ }
/* 'j' does exist */

If something like that could work, it would allow sane scoping rules for the vast majority of cases, while still allowing them who wanted to take aim at a foot and pull the trigger to do so.

CyrusNajmabadi commented 7 years ago

I've looked into this bit, and i'm thinking it's not a good approach going forward:

and change the implementation of the is int x and out int y "explicit variable declarations" to be "implicit variable declarations". Meaning, that if x exists, is in scope, and is an int, use it.

From talking to >10 devs, (backgrounds in C/C++/Java/C#) every single dev found it unwelcome and surprising that such a declaration would implicitly use a declaration already in scope. They either expected a new variable that hid the existing variable, or a compiler error for the clash (even when the types matched).

Cheers!

CyrusNajmabadi commented 7 years ago

If anyone needs the "leaking" functionality of #12939, no changes need to be made. We have that ability in C#6 and earlier, where you just explicitly declare the variable in the scope that you need it.

There is a suitable large number of requests that people be able to use the new "out var" feature to replace code that used to explicitly declare the variable before use. As such, the above point no longer holds. People do want the ability to use 'out var' with similar scoping to how they used to have to use 'out' before.

Talking to these people, they find it cleaner to do things this way, and find the scoping intuitive as it simply matches the scoping that they were used to. In other words, 'out var', to them, is just a simpler way to write the feature they have today. Having 'out var' have limited scope lessens the feature for them as it makes it too restrictive for the code cases they have today with 'out'. And, if the new feature doesn't fit nicely into the code they have today, and doesn't cleanly let them move that code forward, then they don't view the feature as an improvement for them.

As such, i think it's worthwhile that the feature be useful to the largest set of users possible. Wide-scoping provides that. It enables the feature for people who want to move existing code forward, while not restricting hte feature from those who would prefer narrow scoping. The only onus on the narrow-scoping usage is that fresh names be picked. In other words we had these options:

  1. Narrow scoping. Prevents usage from users who want wide scoping. Allows usage without name collisions for users who want narrow scoping.
  2. Wide scoping. Allows usage from users who want wide scoping and who want narrow scoping. But requires those who want narrow scoping to pick fresh names.

In the interest of making the feature widely applicable and useful to all users (not just those who prefer narrow scoping), we went with '1'.

Thanks!

CyrusNajmabadi commented 7 years ago

For those who like it leaky:

Your solution is not pleasant to those who want wide scoping. There are people who want wide scoping without the need for extra statements. We think the solution provided so far can provide that without being unduely unpalatable for those who still prefer narrow scoping.

CyrusNajmabadi commented 7 years ago

Remember, it is our duty as developers to question anything, and everything,

Please continue questioning anything you don't like and providing feedback as to your POV. It's really appreciated and helps a lot when we get together to design and make decisions on these sorts of things. By getting feedback and information from a wide variety of sources we can truly work toward solutions that we think will be hte best for the community overall. Note that even if we think something is best overall, that doesn't mean we think that everyone will like it :-) We're fully cognizant that there have been nearly zero changes we've made to the language that have been received positively by the entire community. Nearly every change is unpalatable to some (including me sometimes :D ).

that someone is trying to force down our throats.

Ultimately, we will make a decision here, and some developers will be unhappy. Were we to take the narrow scoping approach, then there would definitely be some upset developers. :) At some point we end up taking in all the feedback and deciding on what to do (which occasionally includes not doing anything). But, if we feel like we understand the space well enough, have received more than enough feedback to make a decision, and feel the overall feature will be a sufficient net positive, then we move forward on it.

For those who do not like the decision, i can see why you might describe it as something being "force[d] down our throats". However, please recognize that with pretty much any decision, some group is going to feel that way. Unless 100% consensus can be reached on new feature (which we've literally never been able to get given that there are groups that are adamantly against any new features at all), some group will always have the language go in a direction they do not want. While i'm sympathetic to being on the receiving end here (after all, i've routinely been there myself), it's not something that should prevent change if we think the overall benefit is worth it.

Thanks! And please keep the great feedback and ideas flowing :)

CyrusNajmabadi commented 7 years ago

Narrow scoping of the is operator is of great importance to web developers. We get values from many different sources, usually boxed, and need to convert them to a specific type, which completely breaks with wide scoping.

Could you clarify this bit. What scenario 'completely breaks'? Thanks!

CyrusNajmabadi commented 7 years ago

By forcing the is operator declaration to be Explicit, and leaky, you leave absolutely no options for those who want a narrow scope.

Yes. that is understood. If you want a narrow scope, you can't have that. But you can at least still use the feature and not take advantage of the wider scope if that's not of any interest to you.

The converse is not true. If we made the scope narrow, then there would be no option for those that want a wider scope. They would be locked out of the feature.

I'd rather have both camps able to use the feature, even if suboptimal for one camp. Cheers! :)

CyrusNajmabadi commented 7 years ago

Breaking scope in the way that you are proposing is beyond ridiculous.

How is scope broken? Every existing C# 6 scope works exactly the same way as it used to. We're only adding a new feature and defining precisely what we want the scope of that to be. It's unclear to me how anything is 'broken' here :)

as it appears that you guys just don't get how damaging this would be to the language

Given how many people we've talked to who don't think anything is breaking here... it's not clear at all that there's any sort of consensus that what you're talking about it happening :)

--

Heck, we've made scope choices in C# in the past that were unfortunate (like how foreach scopes worked). We even were willing to change them (including accepting the breaking change fallout). At the end of the day, the sky was not falling. No 'partners' (enterprise or other) were really 'put out' by these decisions :) We're simply talking about a facet of a language feature. But it seems to have taken on life as something of life or death consequences :D

CyrusNajmabadi commented 7 years ago

How? If we require narrow scoping, how can we use the feature?

It's not clear to me how 'narrow scoping' is 'required'. :) I think that's the disconnect.

HaloFour commented 7 years ago

I bet Microsoft assumed that they were making the "right decision" when they violated these simple tenets previously with C, only to have to follow up with breaking changes and yet another dialect. I still know organizations that cannot migrate off of Visual C++ 6.0 because of those bad decisions.

The syntax and grammar that C# chose to adopt comes with an assumption of behavior, including scope. Sure, C# has deviated in a variety of areas, particularly where it beat C++ to the punch (if var were a new proposal today I would be arguing that auto be the keyword). This is of the biggest deviations and directly contrary to the existing behaviors that have previously been hammered out through standardization. These arguments have all already happened.

Apart from the insanely late timeframe of these changes (seriously, you're talking RC2 here, there should be nothing even remotely up for discussion), what's worse is that this decision pollutes the entire future of pattern matching in the language. All because a few lazy developers don't feel like predeclaring their variables?

CyrusNajmabadi commented 7 years ago

All because a few lazy developers don't feel like predeclaring their variables?

Please be respectful. No one is disparaging you for the style of code you like. :)

We have many developers who use are product and who evaluate things on a wide variety of axes. One particular developer (or group)'s usage of the language is likely to not at all be representative of the whole. Our goal is to evaluate our features against the broad spectrum of our users and come up with balanced approaches that we think will maximize the overall benefit to the larger ecosystem.

CyrusNajmabadi commented 7 years ago

Apart from the insanely late timeframe of these changes (seriously, you're talking RC2 here, there should be nothing even remotely up for discussion)

We do not agree. We set our on schedules and we feel there is more than enough runway to both continue working and implementing language features. I'm sorry you feel otherwise. However, the control of the C# schedule is in the hands of the LDM and the compiler team. Both of these groups feel like there is enough time for the work we are doing. Both for getting implementation quality high enough, and for vetting if the language choice meets the goals we're setting out.

CyrusNajmabadi commented 7 years ago

I bet Microsoft assumed that they were making the "right decision" when they violated these simple tenets previously with C

I don't know what "simple tenets" you're referring to :). The LDM has a ton of experienced minds who are deeply familiar with dozens of languages. This choice doesn't seem to be particularly contentious or out of line with anything we've seen elsewhere.

CyrusNajmabadi commented 7 years ago

This is of the biggest deviations and directly contrary to the existing behaviors that have previously been hammered out through standardization.

What are you referring to specifically here? Thanks!

HaloFour commented 7 years ago

@CyrusNajmabadi

Please be respectful. No one is disparaging you for the style of code you like. :)

They do all the time, and that's fine. Where that stops being fine (for me) is where it becomes legislated as part of the language. I call into question the motive behind these decisions.

This choice doesn't seem to be particularly contentious or out of line with anything we've seen elsewhere.

What are you referring to specifically here? Thanks!

It's completely in opposition to the standardized behavior of C++. I would hope that the LDM has at least a passing knowledge of that language.

CyrusNajmabadi commented 7 years ago

They do all the time,

Two wrong and all that :)

and that's fine.

No, it's really not. We're adults here and it's paramount that we all be able to treat each other, and the greater C# community with respect. We don't disparage other developers because they use code patterns we find personally don't like. It's inappropriate for this sort of discussion. Thanks!

It's completely in opposition to the standardized behavior of C++. I would hope that the LDM has at least a passing knowledge of that language.

Could you be specific? Which specific behavior of C++? Can you point me to the relevant section you're referring to? I'm happy to take a look. But i don't have the time to simply slog through the entire spec trying to guess at what you might be referring to.

Thanks!

Where that stops being fine is where it becomes legislated as part of the language. I call into question the motive behind these decisions.

Uh... :) Not sure what to say about that. AFAICT, the motives are pretty simple: Change the language in ways that we think will be an overall benefit to the great C# and .net ecosystem.

HaloFour commented 7 years ago

@CyrusNajmabadi

Could you be specific? Which specific behavior of C++? Can you point me to the relevant section you're referring to?

I don't know the exact part of the spec, but my understanding is that any C++ loop/condition construct that permits for the declaration of variables within the condition that a new scope is established within that condition. For example:

while (int i = get_data()) {
}
// i is out of scope here

if (int i = get_data()) {
    // i is in scope here
}
else {
    // i is also in scope here
}
// i is out of scope here
CyrusNajmabadi commented 7 years ago

Ah, you're referring to declarations of variables, not in an argument context. That doesn't seem to be the same feature that we're talking about for out-variables. Note: for C# statement constructs that do allow local variables to be declared, we're keeping the same scoping for out-vars.

To keep things simple, we're keeping Pattern variable scoping the same as out-var scoping. We got lots of feedback that people did not what these to be different.

HaloFour commented 7 years ago

@CyrusNajmabadi

Ah, you're referring to declarations of variables, not in an argument context. That doesn't seem to be the same feature that we're talking about for out-variables.

out var is the declaration of a variable. The fact that it's then used as an argument to a function isn't relevant, only the location of the declaration is.

The fact that while and if couldn't introduce new variables before didn't imply the nature of the scope to which said variables might be contained. That was directed by the syntax inherited from C++, but constrained by C#, not dissimilarly to how C# also constrained local shadowing or implicit case fall-through.

And speaking of switch, that is the example that torpedoes the entire argument about retaining existing scope behavior. Obviously a completely different discussion and decision happened there.

I would agree that out var and pattern variable scope should be consistent with each other. But pattern matching is a massive and significant shift to the paradigm of the language whereas out var is barely a "nice-to-have", less novel or helpful than most of the lower-hanging fruit proposed here. I'd rather out var never be implemented than to drag pattern matching along with it.

CyrusNajmabadi commented 7 years ago

And speaking of switch, that is the example that torpedoes the entire argument about retaining existing scope behavior. Obviously a completely different discussion and decision happened there.

Switches couldn't have expressions in case labels that used 'out' :)

CyrusNajmabadi commented 7 years ago

The fact that while and if couldn't introduce new variables before didn't imply the nature of the scope to which said variables might be contained.

Note: while and if statements could never introduce variables, and that behaviors remains today. Rather, poatterns and out-vars introduce variables. So nothing has changed here.

for and foreach statements can introduce variables. And we use the well defined scopes those constructs already have.

Perhaps that's the disconnect here? You see this as statements introducing variables, and deviating from where you'd like teh statement's variables to be placed. Otherwise see it as expressions declaring variables, and placing them in the current understood C# scopes for those things.

I hope that helps clear things up!

CyrusNajmabadi commented 7 years ago

I'd rather out var never be implemented than to drag pattern matching along with it.

Your feedback is noted. But it certainly not felt universally :)

Indeed, we've heard about out and how people want it to be improved than patterns. Patterns themselves seem to be much more niche. Whereas 'out' affects pretty much all existing customers and how they use the existing .Net fx. As such, we def want a solution that benefits all the people who have been wanting this for so long.

Thanks for the input! It's been very helpful :)

CyrusNajmabadi commented 7 years ago

out var is the declaration of a variable. The fact that it's then used as an argument to a function isn't relevant, only the location of the declaration is.

Yes. And we scope the vairable into the scope that the declaration is in :) We're not generating any new scopes**. So the scopes of C# remain and out-vars fit into them like current variable decls do.

--

** Technically this is not true. Clearly case labels have been granted a scope. But we felt that was ok because there could be no conflict with existing code. in other words, we could act as if there was always a scope there in the past. Also, because we want to convey the idea that the presence or absence of blocks shoudl have no effect on certain statements, we made it so that embedded statements acted as if they had the same scope as if you had a block there. This way removign/adding the block would not change semantics. :)

in both cases, you could not observe the difference. So we're treating it as if we went back and stated that C#6 and earlier had these scopes. It would not effect any existing programs, but it enables sets of programs we feel are valuable for C# 7 and onwards.

We felt, overall, that these were pragmatic choices that did not prevent anyone from using out-var if they wanted narrow scopes. But ensured that out-var would work well for those who wanted wide scopes.

I hope this helps clear things up! :)

HaloFour commented 7 years ago

@CyrusNajmabadi

Perhaps that's the disconnect here?

The disconnect is that in the C++ family of languages the statement provides the child scope in which those expressions are contained. Variables introduced within the statements don't "leak" out. The LDM is making this decision that the statement has no scope of its own only based on the fact that C# didn't permit it before.

Indeed, we've heard about out and how people want it to be improved than patterns. Patterns themselves seem to be much more niche. Whereas 'out' affects pretty much all existing customers and how they use the existing .Net fx. As such, we def want a solution that benefits all the people who have been wanting this for so long.

Big shock, ask some developers of C# about features that don't exist in C# and you don't get a lot of excitement or understanding about it? Those same people questioned async and would have traded it in for a way to shave off three keystrokes elsewhere. Patterns are niche in C# because C# doesn't have patterns. And if you're basing it off of the excitement of "pattern matching" in C# 7.0 that might have to do with how significantly neutered it is compared to the larger proposals which I seriously hope are still slated for a future version of the language.

Clearly case labels have been granted a scope. But we felt that was ok because there could be no conflict with existing code.

Switches couldn't have expressions in case labels that used 'out' :)

Still can't, yet. But that still doesn't explain the logic as to why a pattern variable in a case label has a different (and more constrained) scope than a variable declared directly under that case label.

Can you name another language that has scoping rules anything like this? Where a child scope can be defined for a single clause yet variables declared within leak into part of the body of the parent scope?

I hope this helps clear things up! :)

Do you have any idea how patronizing this sounds? I get that you're trying to be friendly, but it really doesn't come off as such. Text is really an awful medium.

CyrusNajmabadi commented 7 years ago

Big shock, ask some developers of C# about features that don't exist in C# and you don't get a lot of excitement or understanding about it?

I didn't claim it was shocking. I was just explaining that it's more niche and people care deeply about the bread and butter features they work with day in and day out. This was in response to: "I'd rather out var never be implemented than to drag pattern matching along with it."

We considered this but felt the overall sentiment indicated that others would rather us do this. I wasn't stating this as a surprise, just as way to help clarify that your individual position should not be considered representative :)

--

In general, that's a very difficult thing that people have to deal when discussing languages. Individual biases affect people a lot. And it's very common (heck, the norm), to see people focused deeply on the stuff they care about, and dismissive of areas they care less about. However, for any individual customer, the set of things that are cared about will be very different. It's helpful to keep that in mind when trying to evaluate this stuff. Thanks!

CyrusNajmabadi commented 7 years ago

Do you have any idea how patronizing this sounds? I get that you're trying to be friendly, but it really doesn't come off as such.

I'm very sorry about that! I received feedback from other people on github that they didn't like when i didn't do this. It made them feel that their feedback was not appreciated or that they weren't being listened to. I've been trying to separate out the different concerns when talking to people. Letting them know that just because there are disagreements doesn't mean that their feedback isn't being closely listened to and weighed into the continual evaluations we have as we discuss things week in and out :)

CyrusNajmabadi commented 7 years ago

But that still doesn't explain the logic as to why a pattern variable in a case label has a different (and more constrained) scope than a variable declared directly under that case label.

I personally agree with you here. I really dislike switch scoping in general. A big issue is that if we reuse switch (which i was on the fence about to begin with***) we can't change the scoping under the case label. 'cases' were interesting because we had three options we coudl go with:

  1. Have the case scoping be the same as the scope under the case.
  2. Have the cases have their own unique scopes.
  3. Have hte cases be scoped into the scope that the switch was in.

Each of these had pros/cons. '1' was consistent with case bodies, but very restrictive. '3' allowed us to not introduce a new scope in the compiler (but was neutral in terms of how we wanted to backport scopes to earlier versions of the language). '2' had the benefit of at least enabling the ability to reuse names multiple times (which seemed at least nice to have for some customers) while not restricting anything.

In effect, making this scope wider than '1' enabled a scenario that people seemed to like, without restricting any real world cases that people could bring up.

In effect, it was a pragmatic decision** :)

--

** I want to emphasize this bit as i think it sometimes gets lost with the hundreds of messages going back and forth. When i started on C# i was a very 'ivory tower' developer :) I disagreed with so many decisions being made in C# because i felt tehy were impure, or inconsistent. However, over 10+ years of working on this language i've grown a deep appreciation for the pragmatic approach the LDM has taken on many decisions. While not perfect, they tend to value facets of the language that people then really appreciate moving forward. I started to see that my own perspective was optimizing to optimize toward levels of consistency that would end up making the language less pleasant to use in practice.

With 'switches' this is what we've attempted to do here. An approach that enables certain patterns that we think is valuable, while not really restricting any code we think will arise much in practice. In effect, this approach drives a lot of decisions we make. We're always able to find esoteria where our language decisions fall over. However, it's often that those cases are ones that will be incredibly rare in practice (including being something that will never be seen). In those cases, i'm ok with optimizing the language for the cases we strongly believe will be more widepsread over those that will be very rare, or will never appear.

Does that help clear things up? Thanks!

--

*** I was a strong proponent for not reusing switch and just having a new match statement and match expression. But, it was felt to be better to reuse 'switch'. We went into that knowing htat it would lead to some really wonky stuff due to the heritage of switches that we already opted into when we adopted switches in c# 1.0.

CyrusNajmabadi commented 7 years ago

Still can't, yet.

Yes. But they can have pattern variables (and we discussed previously the desire for pattern variable and out-var scoping to be the same). We felt there were valuable code patterns enabled by this, and no reasonable code patterns hurt by it.

So switches are wonky... true. But we inherited much of that wonkyness in C# 1, and we can't change it. This was an approach that accepted that wonkyness, but enabled a code pattern we thought was valuable enough.

CyrusNajmabadi commented 7 years ago

@Kaelum I wanted to address this point:

When you need temporary variables, which covers approximately 99% of our usage of out variables,

I was curious about what the actual percentage would be. I started by looking at the Roslyn codebase (which has already been adopting C# 7 features). I analyzed all the current usages of 'out var' to see how they were used.

There are currently 1969 usages of out-var in the Roslyn codebase. I used Roslyn to analyze every local generated by these out-vars. I bucketed each out-var into one of two cases:

  1. the local declared is only used within the containing statement.
  2. the local declared is used outside of the containing statement.

The counts were:

Used-only-inside: 821 cases Used-outside: 1148 cases

This was interesting to me as the number of cases used outside was greater than the number used inside. Nearly 60% of all cases are ones where we use the variable outside of the containing statement! My personal guess that it was only going to be 20%. Being 3x that was not what i expected :)

So, i'm going to push-back against the claim that narrow-scoping "covers approximately 99% of our usage of out variable". From our very own code, narrow scoping would only cover 40% of all cases. Designing out-var such that it wouldn't work for 60% of the cases we currently use it for would be a pretty bitter pill :-/

CyrusNajmabadi commented 7 years ago

I found another ~1400 cases that weren't being checked. For a total of 3389 cases. crazily enough, the end numbers are:

Used-only-inside: 1357 cases Used-outside: 2032 cases

Which continues the pattern of 40% 'used only inside the statement' and 60% 'used outside the statement'. For our own development practices, it seems like we consistently use 'out' in wide-scoping cases 60% of the time. Fascinating!

DavidArno commented 7 years ago

@CyrusNajmabadi,

I really don't understand what you are doing here. The decision is made to change the C# language scoping rules to make the out var feature slightly easier to use. Despite a huge range of opinions here, we're almost unanimous in our condemnation of this decision. Almost the only voices (outside of the language team) who like this feature speak to you in private about it, and so we have to take your word that these people really exist. But the decision is made. We all hate it, but we accept it's going to happen. It's a done deal. It's a bad deal; but it's a done deal So why have you appeared here after the thread had been dormant for a month and provoked us all into an argument with you again?

What are you trying to achieve by goading us?

AdamSpeight2008 commented 7 years ago

That's why I proposed earlier, that it should by-default enclosing scope (nearest outer scope aka widening) rather then enclosed scope (nearest inner scope aka narrowing). It simplifies the more common usage case. For the other case it should require explicit declaration (that the scoping is enclosed). For example

 M( out var x )  /* Enclosing scope */`

vs

 M( out let x ) /* Enclosed scope */

If in either case the variable is already in scope, it should be a compile-time error. Thus prevents variable shadowing and accidental mal-usage. As existing constructs / code patterns let you use the existing variable.

examples

Enclosing Scope

int x = 0;
switch ( obj )
{
  case obj is int x:
    /* use exist variable */
  case obj is double x:
    /* (error) */
}
switch ( obj )
{
  case obj is int out x:
    /* declare x into enclosing scope */
  case obj is double out x:
    /* (error) redeclaration of x is not allowed */
}

Enclosed Scope

switch ( obj )
{
  case obj is int let x:
    /* declare x into enclosed scope */
  case obj is double out x:
    /* declare x into enclosed scope  */
}
int x = 0;
switch ( obj )
{
  case obj is int let x:
    /* (error) redeclaration of x is not allow */
  case obj is double out x:
    /* (error) redeclaration of x is not allow */
}

Additional out var x and out let x also work for introducing variables in expressions.

CyrusNajmabadi commented 7 years ago

the thread had been dormant for a month

This thread was linked to from https://github.com/dotnet/roslyn/issues/12939 (which still is under active discussion). I was collecting information and positions to see if anything was being missed. I realized there were a few points made here that hadn't been covered, so i thought it might be useful to discuss them :) There had been no language designer input here, and i thought there was enough interest in the topic to warrant some information on at least how one of the LDM partcipants was thinking about things :-)

I really don't understand what you are doing here.

Discussing the changes with community members :)

What are you trying to achieve by goading us?

I'm trying to do no such thing. I came to discuss the issue because i find language design fascinating and i absolutely love conversing about it with anyone interested in the topic. It's why i work on the language and one of the things i love about this forum. I thought the data and information i could provide would be helpful.

And, frankly, it has been to me. The conversation with HaloFour helped me better understand one of the objections to the feature. The more communication, the better understanding, and the better informed we can be when making future changes :)

Now, if you don't want more discussion in this thread, that's fine. But if that's the case, we shoudl probably close this. I thought there was value in discussing this topic so i felt like it was worth participating :)

AdamSpeight2008 commented 7 years ago

@DavidArno I think the issue is that the community think the change is wrong, as the "actual" scoping is subtle and change depending on context. Which could easily be missing when reading the source code. The community would like to be give to option to decide what scope it should in there own code.

CyrusNajmabadi commented 7 years ago

@AdamSpeight2008 I would not be opposed to a future language feature that limited scope to, say, the containing statement.

That might provide a best-of-all-worlds for people. People who desire wide-scoping can get it, but those who prefer narrow scoping could opt-into that as well.

AdamSpeight2008 commented 7 years ago

@CyrusNajmabadi I'm thinking of the "average" - professional C# programmer would expect to the code to do. It removes the potential ambiguity (even if that ambiguity "technically" doesn't exist).

CyrusNajmabadi commented 7 years ago

hrmm... good question. My gut tells me they would expect to be able to use the variable. That restricting scoping of a local would be 'nice to have', but not really something they would care first and formost about.

DavidArno commented 7 years ago

@AdamSpeight2008,

I completely agree that, even if one accepts there is merit to out var, then accepts there is merit to leaking scoping, the fact that these variable declarations leak in different ways depending on the language construct will likely cause great confusion. But, again, the decision is made. Yes it's the wrong decision, but we have to lump it.

AdamSpeight2008 commented 7 years ago

There will be only a couple of exception, which they should be aware of that of for and foreach, which can't be easier changed without breaking existing code.

It also easier (for me at least) to understand in direction (aka scope) the variable is going to available from

Enclosing (Widening)  <<-- out var
                           let var -->> Enclosed (Narrow)
AdamSpeight2008 commented 7 years ago

@DavidArno Personally I'd change it now, so it doesn't be come another billion dollar mistake.

CyrusNajmabadi commented 7 years ago

There will be only a couple of exception, which they should be aware of that of for and foreach, which can't be easier changed without breaking existing code.

I don't even see it much as 'exceptions'. for/foreach already clearly introduce variables today. So i think it will be intuitive to users that out-vars for those constructs go in hte same scope as the existing iteration variables. Things like 'if/while' don't introduce variables, so the variables declared there go into the same scope that they would have gone into originally.

CyrusNajmabadi commented 7 years ago
Enclosing (Widening)  <<-- out var
                           let var -->> Enclosed (Narrow)

This approach would also have some slight symmetry with javascript, as they used 'let' to denote a block scoped variable.

AdamSpeight2008 commented 7 years ago

@CyrusNajmabadi Is there an example, which show that use-case. As I can't think of one.

CyrusNajmabadi commented 7 years ago

@Kaelum

I will send the private email that you requestted, this week.

Thanks!