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
The chain operators don't work with flow-control statements exit, return, and Throw #10967
Comments
cc @rjmholt |
Yes the PowerShell committee discussed this and it was decided against -- the RFC was updated to reflect that and it was taken out. Given the syntactic complexity of it, I think it was actually the right decision. There was a lot of inconsistency in the edge cases of this proposal. Instead, you can use a subexpression pretty easily: |
Also see: PowerShell/test/powershell/Language/Operators/PipelineChainOperator.Tests.ps1 Lines 92 to 94 in 70ab772
|
I did advocate fairly strongly for being able to use control flow statements in pipelines; I liked the idea and I think the concept is a nice one. Even though it didn't go in, I think it got a fair run. But syntactically it doesn't work because the big difference between PowerShell and bash is that in PowerShell statements contain pipelines, whereas the opposite it the case in bash. Trying to shoehorn certain kinds of statement where a pipeline fits just made the syntax really unhelpful, and you'd either have to make dodgy syntax breaking changes or implement very fiddly syntax rules to massage it in. The (badly named) subexpression syntax is designed for embedding a statement within a pipeline, so it offers this neatly. |
I wonder how the fact "syntactically it doesn't work because the big difference between PowerShell and bash" can we made clear to users. I had assumed that this new feature would basically work the same in PowerShell as in bash, hence many others will very likely start with the same assumption. At least this conclusion seems 'another nail in the coffin' for the goal 'pwsh is a replacement to bash' :( |
But it does work: { Get-Item /nosuch || $(return) } | Should -Not -Throw
{ Get-Item / && $(return) } | Should -Not -Throw |
@rjmholt if that already works, I don't see it being especially sensible to mandate the awkward syntax. |
Prior to pipeline chains, the grammar was (I’m using a simplified shorthand here):
The current grammar just substitutes pipeline chains for pipelines, so that a pipeline is just a pipeline chain of length 1:
If we allow control flow statements, then you might have this:
That gives us back
The biggest question is “what do I expect those to do” and if there’s no clear answer it’s not good syntax. So let’s fix it. Putting control flow statements only at the end of chains fixes nothing because they already enforce themselves as the ends of chains; once you use one, the chain to the right is subordinate to So let’s fix it instead by only allowing unchained pipelines under Ok we can fix that if we allow chains when And we still haven’t addressed how to deal with And we also still haven’t addressed how to deal with control flow statements in places where only pipelines working as expressions used to be expected. In my original implementation that last was addressed by having two kinds of AST, so that So it’s a bit like the conversation Roy Batty has with Eldon Tyrell; every attempt to save the situation has some ugly consequence. To save that one tree we have to move the forest around it, and we’ve introduced more problems. A programming language’s grammar is a human interface that must be above all else consistent and composable. Inserting chaotic corner cases into the grammar makes it impossible to reason about or teach. And PowerShell needs to be defensible in 5 or 10 years time. |
Right, but if it's already possible to make it "work" by using subexpression we should probably make an effort to formalize it in a way that is reasonably intuitive. Otherwise, the behaviour of such subexpressions will always be pretty ambiguous, unless you intend to completely disable keyword use in a pipeline chain. |
An alternative is to make sure we include flow-control subexpressions in the examples for the documentation of this operator. Sub expressions is an existing concept and maybe users should use it more. |
@SteveL-MSFT, adding this to the documentation would be great! A search for the term gives 0 results currently! |
@PowerShell/powershell-committee reviewed this and we agree to that we will stay with what we agreed in the RFC which is that supporting flow-control statements complicates the implementation and using sub-expressions is the right way to use this. We should improve our documentation on sub-expressions if PowerShell users are not using them. |
@SteveL-MSFT, a quick tangent, though I believe it reinforces the point that we shouldn't force the awkward
If anything, users should be using the (unfortunately named, as @rjmholt pointed out) sub-expression operator (
|
This is from the early days of PS but IIRC |
Thanks, @rkeithhill. The latter two definitely officially have that name in By coincidence, grouping operator is what I'm proposing in MicrosoftDocs/PowerShell-Docs#5154. |
It was called that in the first edition of Bruce Payette's "Windows PowerShell in Action" section 5.3 (page 119). And it's the same in the second edition (page 156). |
Hmm, in the second edition, it looks like "grouping expressions" refer to the set of three operators. It goes on to say:
For subexpressions it says:
So maybe it is just |
Thank you for putting up a good fight and for laying out the rationale. Before I even try to understand the intricacies of the grammar, let me make a plea purely from a user's perspective: If we force the use of
As an aside: The same arguments apply to the unfortunate decision to require So, for the sake of the end-users' experience, can we look for a solution that works as expected, is easy to explain, and technically not too tortu[r]ous? Personally, I'm not worried about cases such as |
I appreciate the pointers, @rkeithhill: I found "simple parenthetical notation" in that chapter for In other words: I encourage you to join the conversation at MicrosoftDocs/PowerShell-Docs#5154 to either endorse the suggested grouping operator name or to suggest alternatives. |
I very much understand and even sympathise, but the problem is that the grammar is the language. It's our most fine-grained API and also our primary user interface. PowerShell has pipelines contained by statements, not statements contained by pipelines. That's always been the case, and the way to put a statement within a pipeline is to use the Essentially, with such a syntactic change you're asking for a new language, with a different treatment of syntactic and semantic constructs like expressions, pipelines and statements.
Just to reiterate, it's not artificial. This isn't a case of us setting the rules because of an opinion. Programming language syntaxes are formal languages with specific requirements. In our case, PowerShell is (mostly...) a traditionally LL(k)-parseable grammar (like most interpreted languages, since this is a big asset when you're reading the program on the fly). As the parser proceeds from left to right in the program, it decides what syntactic element it sees based on the state it's currently in (captured in our recursive descent parser mostly by the method we're in) and the element it sees right now. As the parser proceeds within each state, it has some way of deciding when to move to the next state or when to return to the state it came from. For example, in The problem arises because pipelines, being the default form of statement, terminate the same way as arbitrary statements. So when you embed a statement within a pipeline, it's not clear what you're terminating, unless you have a syntax to delineate the extent of the statement. That's precisely the purpose of That's true for any implementation that removes a container syntax for statements in pipelines or pipeline chains, so it's not an implementation detail. In fact, if PowerShell used an LR parser, it would be a major issue in the parser (a reduce-reduce conflict). To quote that link from the GNU Bison docs:
This is particularly important because syntaxes aren't a concept required by computers (those do fine with finite-block input like byte code and machine code). Syntaxes are a human interaction requirement, and a flaw in the syntax is as much a usability issue as it is a technical one. We could write the parser to make a call on the grammatical ambiguity (like C does), but it would still be confusing to users to pick apart
I agree that the scenario is similar, but also think there that not breaking the way we parse existing valid syntax was the right way to go. Again, a change there isn't just a case of us making a decision to support a small number of wild programs out there — we've made numerous breaking changes in cases where we thought the pros outweighed the cons (not that I personally agree with all of them, or that those do or don't justify others). The issue is that changing some aspect of the tokenizer or parser means that two PowerShell versions will no longer generate the same AST for some scripts, so two PowerShells will "see" the same script differently. That means we can't even read the syntax the same way, so there's no PSScriptAnalyzer rule you can write to pick up possible issues around it; PSScriptAnalyzer will see a different syntax from one PowerShell version to the next. Unlike C#, PowerShell can't pre-compile a different syntax to a compatible format for later execution, meaning that a syntactic change is tethered to the runtime. Once PowerShell makes a syntactic break, we reduce the number of scripts that can work against different versions, meaning users are forced to write two scripts, which is a serious issue for a shell and a scripting language. PowerShell is supposed to be dynamic enough to bend past all the differences with logic inside one script, but the syntax is the one non-negotiably static thing we have. And making a syntax change where both before and after are valid is especially pernicious, since there's no simple way to detect it, there's no warning for it and even after executing it in both environments, users might not know that a different behaviour occurred. |
I appreciate the thoughtful, in-depth answer, @rjmholt. Re the |
0 && return 1
0 && 1
Should I understand that |
@yecril71pl I'm not sure I understand the question. Can you elaborate? |
Why is |
So in that parsing mode any
The term expression here refers to a specific subset of language elements. In this case, it's a constant expression (the literal integer So basically because statements cannot be parsed, the snippet
It just means an executable file found by command discovery. In Windows it means a file in the Portable Executable format, or just anything that can be invoked with the shell verb "Invoke" (which is pretty much any file system item). |
The error message I get gives me a specific subset of that subset, and that subset does not cover
I do not think it should be parsed as a command because
So PowerShell has its own definition what "operable" means. We should definitely change that. |
Not sure what you mean. If you'd like clarification on something I've said feel free to ask.
Anywhere where statements are not allowed it will be parsed as a command. If you'd like to know more about why statements are not allowed in this syntax, please read through the rest of the thread. After that if you have questions about a specific part feel free to ask.
That's out of scope for this issue. If you'd like to discuss that I'd recommend opening a new one. |
I think it is a wrong thing to do, UX-wise. I would expect |
Yeah I agree. Problem is that logic is very heavily relied on with the |
So, returning to the problem at hand, if we allow |
Believe it or not plenty of folks do. Especially if they are taking a
You got it. Not only would that no longer work, it would be a parse time error :/ |
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. |
2 similar comments
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. |
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. |
This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes. |
It's definitely not completed... |
A common idiom (in the Bash world, which inspired PowerShell's
&&
and||
operators) is to conditionally exit a script when invocation of a command fails, along the lines of:Currently, neither
exit
norreturn
northrow
can be used on the RHS of&&
/||
Steps to reproduce
Expected behavior
The tests should pass.
Actual behavior
The tests fail, because
return
is not recognizedEnvironment data
The text was updated successfully, but these errors were encountered: