Skip to content

Do not require { ... } (curly braces) around variable names for null-conditional member access #14025

Closed

Description

Note: This issue is just a reminder to reopen #11379 in order to have it revisited by the PowerShell committee; the willingness to do so was signaled in the Sept. '20 community call (see details below), but there was no follow-up, and leaving an issue in a closed state usually means that it falls through the cracks. There is clearly community interest in having this revisited, and the analysis that led to closing the issue is disputed.

Once #11379 is reopened, this issue can be closed.


Steve Lee (@SteveL-MSFT)

Thanks for discussing issue #11379 in the September community call (https://aka.ms/PSCommunityCall; as of this writing, the September call's recording has not been posted there, but is accessible at https://aka.ms/JoinPSCall, until the next call), starting at 46:00, based on Thomas Nieto (@ThomasNieto)'s suggestion in the prior solicitation for discussion topics at PowerShell/PowerShell-RFC#260:

(I was hoping to provide a direct link to the relevant portion of the recording, but that will have to wait until it is posted to YouTube and linked to at https://aka.ms/PSCommunityCall.)

Based on your remarks in the recording:

  • Given that you indicated willingness to revisit this, please reopen the linked issue and re-tag it as Review - Committee.

  • As for your remarks that enclosing variable names in curly braces is a preexisting feature and that people object to having to use them in this case on aesthetic grounds:

    • I don't think anyone here isn't aware that {...} can and sometimes must be used to disambiguate variable names.
    • It is also not about aesthetics, it is primarily about not getting the expected behavior in manner that potentially fails quietly (depending on what strict mode is in effect), due to not expecting to have to use {...} in this case; a secondary concern is typing inconvenience.

Activity

  1. changed the title [-]Feature Request[/-] [+]Do not require { ... } around variable names for null-conditional member access[/+] on Nov 10, 2020
  2. ALIENQuake commented on Nov 28, 2020

    @ALIENQuake

    FWIW, if you use PowerShell script with ${var} syntax (not necessary with ? char) in combination with Terragrunt, it will fail. A workaround is to escape ${ with $$${ which is very misleading and looks ugly.

  3. SeeminglyScience commented on Nov 29, 2020

    @SeeminglyScience
    Contributor

    ALIENQuake A (subjectively) better workaround is using ($var)?.Member instead. Personally I prefer that syntax over ${var}?.Member anyway, assuming the only intention is to make ?. work.

  4. mklement0 commented on Dec 11, 2020

    @mklement0
    ContributorAuthor

    @joeyaello, thanks for showing awareness of the problem in the Nov 19 community call:

    To be honest, this kind of did sway me on this issue a little bit, we still want to have a conversation and we can make it a priority in our next committee meeting. Someone did do the analysis on our behalf in the corpus and determined that there was 2 uses of the question mark in a variable name. I think it's something that we could possibly do as an experimental feature early in 7.2 [...] it's tricky [...] it's not a zero-cost thing [...] not a huge priority right now, so I don't want to guarantee that that's happening.

    Priority is a separate issue, but I do hope this issue won't fall through the cracks, which seems to be a real possibility, given how the debate has unfolded (see timeline below).

    To frame the issue in the context of the breaking-change debate:

    • The benefit of the change is: Without it, the utility of the new null-conditional access operators will be severely hampered, because users won't expect to have to enclose the variable name in {... } in order to use it; with Set-StrictMode -Version 1 or higher, you'll at least get an error (because $foo?.bar then requires the existence of ${foo?}), but with strict mode off the code will simply not do what users expect, as pointed out by @rkeithhill and acknowledged as a problem by you.

    • As for whose code will break due to the change: if the corpus is representative, virtually nobody's - see below.

    • As for Rob Holt (@rjmholt)'s syntax-change concerns: this is not a syntax change, but a semantic one: old code won't break during parsing, it'll simply behave differently - and since there is virtually no code like that in existence, this is a moot point.


    Timeline:

    Needless to say, the above is liable to erode the community's confidence that its concerns and feedback are taken seriously.

  5. changed the title [-]Do not require { ... } around variable names for null-conditional member access[/-] [+]Do not require { ... } (curly braces) around variable names for null-conditional member access[/+] on Dec 11, 2020
  6. plastikfan commented on Apr 21, 2021

    @plastikfan

    When I first discovered the Null-coalescing operators introduce in v7 I was initially pleased. That is until I started reading the ugly details and starting experimenting with this.

    In my opinion, this is another example of being held back by prior bad design choices that just goes on to haunt us for evermore, instead of making a corrective breaking change. What value is there for being able to use a '?' in a variable name? It has next to no value but high restraining and detrimental impact. Why can't we have a breaking change that removes this? Having to use those {} just to appease variables names with a question mark is a case of the tail wagging the dog. Sure, there will be some code will be impacted. But if you document it well and SHOUT about it, make it easy for users to upgrade their code, possibly via a new new PSSA warning, then I think that would be better for all of us. The use of the {} really is aweful and should be fixed.

    I'm experimenting with test code at the moment and have a test that refers to a PSCustomObject $_options, which contains nested members. I need to access $_options.Tags.From in a safe way.

    Tags is known to never be null, but From is optional. So when you use coalescing, do you apply that to the whole path at once, or do you use the ?. on each sub member. When I try the latter (this scenario is missing from the help doc), it doesnt work because the sub member access is referenced independently, not as a member.

    assuming:

            $_options.Tags = [PSCustomObject]@{
              From = '3.0.0';
            }

    # no coalescing, this is ok, but potentially unsafe, if From doesnt exist (I think)
    
    if ($_options.Tags.From) {

    # this doesnt work
    # => RuntimeException: The variable '$Tags' cannot be retrieved because it has not been set.
    #
    if (${_options}?.${Tags}.From) {

    # this doesnt work
    # => The variable '$_options.Tags' cannot be retrieved because it has not been set.
    if (${_options.Tags}?.From) {

    so what is the correct and safe way to coalesc the entire path from _options right down to From? And also, do the rules apply the same inside of a class method as opposed to a function or script?

    ...and the strange thing is this:

            if ($_options.Tags.To) {
              Write-Host "---> (PLAIN) TO: '$($_options.Tags.To)'"
            }
            else {
              Write-Host "---> (PLAIN) TO: MISSING"
            }

    prints:

    ---> (PLAIN) TO: MISSING

    and in fact this is the functionality I want, but I fear it is unsafe, but I dont know why. And if this works, why do we need the coalesc operator. I though the operator was introduced to prevent accessing a non existing member as in this case, from returning error, but return $null instead.

    PS, I'm using strict mode 1

  7. ExE-Boss commented on Apr 21, 2021

    @ExE-Boss

    Plastikfan (@plastikfan) You need to use parentheses:

    # This works:
    if (($_options.Tags)?.From) {
  8. plastikfan commented on Apr 21, 2021

    @plastikfan

    Well if that works, why are we being bogged down and harrased into using ${}? What is the value of that, if we can just use ()?

  9. ALIENQuake commented on Apr 21, 2021

    @ALIENQuake

    Plastikfan (@plastikfan) The point is to be able to use $variable? syntax, without braces.

  10. plastikfan commented on Apr 21, 2021

    @plastikfan

    ALIENQuake, I know, I'm adding my 2 pence worth to get rid of the braces.

  11. mklement0 commented on Apr 21, 2021

    @mklement0
    ContributorAuthor

    Perhaps needless to say, Plastikfan (@plastikfan), I fully agree with your assessment.

    While using parentheses is always an option, no workaround is actually required in this case, because Tags is a property rather than a variable name, and the former, unlike the latter, aren't permitted to contain ?:

    Set-StrictMode -Version 2
    $_options = [pscustomobject] @{ Tags = [PSCustomObject]@{ From = '3.0.0' }; Tags2 = $null }
    
    # *No* {...} needed around *property* names.
    $_options.Tags?.From # -> '3.0.0'
    $_options.Tags2?.From # -> $null

    But to me this distinction between variable names and property names is yet another reason to never require {...} in combination with null-conditional access (?. / ?[]).


    As an aside: what null-conditional access currently doesn't protect against is non-existence, as opposed to existence with value $null, even though these conditions are otherwise often interchangeable in PowerShell:

    $_options.Tags3?.From # !! ERROR, because property doesn't *exist* - as opposed to *existing and being $null*.
  12. plastikfan commented on Apr 21, 2021

    @plastikfan
  13. morsley commented on Apr 21, 2021

    @morsley
    $_options.Tags3?.From # !! ERROR, because property doesn't *exist* - as opposed to *existing and being $null*.

    Michael Klement (@mklement0) yikes.. what a footgun of a decision

  14. daxian-dbw commented on May 1, 2021

    @daxian-dbw
    Member
    $_options.Tags3?.From # !! ERROR, because property doesn't *exist* - as opposed to *existing and being $null*.

    I have a different opinion on this. By enabling StrictMode, you mean to catch the the cases where a non-existing member gets accidentally dereferenced at runtime, and that's what it does even with the null-conditional operator, isn't it?

    The null-conditional operator is essentially a syntactic sugar of if ($null -eq target) { $null } else { target.member }, so I don't think it should suppress the "non-existing property" check enforced by the strict mode.

    You may ask "why $_options.Tags2?.From doesn't error out given that From is a non-existing member?". That's because the From property doesn't get dereferenced at runtime -- the else block won't be evaluated.

  15. 5 remaining items

  16. vexx32 commented on May 3, 2021

    @vexx32
    Collaborator

    If there is, I'm not seeing it at the moment. Thought there would have been though. #2798 is somewhat related, I suppose. Opened #15338 for that, ty.

  17. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

  18. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

  19. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

  20. ThomasNieto commented on Nov 16, 2023

    @ThomasNieto
    Contributor

    This issue is waiting for the WG to reevaluate with update information.

  21. microsoft-github-policy-service commented on Nov 23, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.

  22. jantari commented on Nov 23, 2023

    @jantari

    Thomas Nieto (@ThomasNieto) commented on this, I don't feel like it should have been closed given that that was "activity"?

  23. vaind commented on Feb 23, 2024

    @vaind

    Can someone reopen this issue? The bot has obviously bugged out. Or should we create another issue to reopen an issue to reopen an issue? 😬

  24. jantari commented on Feb 23, 2024

    @jantari

    I believe the intent with the great issue purging of 2023 was for us to re-open new issues where needed.

    I did that with #20705 so now that issue is in limbo again just like the original #8751 before it 😄👍

  25. vaind commented on Feb 23, 2024

    @vaind

    I believe the intent with the great issue purging of 2023 was for us to re-open new issues where needed.

    I did that with #20705 so now that issue is in limbo again just like the original #8751 before it 😄👍

    that seems to be a different issue?

  26. jantari commented on Feb 23, 2024

    @jantari

    Yes, but it is an example of an issue that was also closed by the bot and we've had to create a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Enhancementthe issue is more of a feature request than a bugResolution-No ActivityIssue has had no activity for 6 months or more

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions