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
Add @@ syntax for inline splatting as hashtable as new experimental feature PSGeneralizedSplatting #10073
Add @@ syntax for inline splatting as hashtable as new experimental feature PSGeneralizedSplatting #10073
Conversation
…ments instead of just hashtable
…ashtable is an argument.
…ith 'Gets or sets a value indicating whether' and a missing newline after a closing brace).
This should probably be marked as an experimental feature. |
|
I'd also like it marked as experimental. Experimental isn't just about risk. It's about acceptance of syntax, workflow, etc., and most importantly, it lets users know that the design of this feature is not yet complete. As expressed here I think |
Also, experimental does not mean it's not going to make it into v7 either. It simply means we're experimenting with some syntax/commands/etc. and want to be able to allow the broader community kick the tires on those things before we sign-off on them as supported. |
@KirkMunro is correct that Experimental means that the design can change and would not be a breaking change. |
Ok, we have 3 changes here:
Happy to add the experimental feature fences around those section after the first review approves the current format. |
@daxian-dbw can probably supply advice here on proper Experimental Feature usage. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are some nice first steps here I think but also some fixes required.
I'm personally skeptical about implementing half a language feature (since there is always a risk that the other half never makes it in and partial grammar changes often break the full scenario from being completed), although looking at the splatting RFC it seems that it does break nicely into two things.
Another thing that should come in as part of this change is completions on the splatted hashtable itself, since completions are such a core part of the PowerShell experience.
The testing probably needs to be built out significantly to cover things like:
- If hashtables are tokenised differently, it will need to cover
@@
used in other places - Failure semantics if the splatted hashtable expression produces a runtime error
- Errors produced if a spatted hashtable is used not as a command argument
- What happens if you try splatting a hashtable to a native command
- Likely a number of other cases I haven't thought about
@@ -1119,17 +1124,19 @@ public static class TokenTraits | |||
/* Command */ "command", | |||
/* Hidden */ "hidden", | |||
/* Base */ "base", | |||
/* AtAtCurly */ "@@splat", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this actually a new keyword? If @@splat
is not expected to appear as a literal token in PowerShell, I don't think this should be here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking of @
as a unary operator.
Thinking that way, one could write @ @{...}
or @@{...}
to mean the same thing, though the later would likely be preferred.
Given that, I wouldn't expect new tokens of the form @@(
or @$
or @@{
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would've put it into the Punctuators
section, similar to AtCurly
but because it's range for the TokenKind
enum is already full, I just decided to put it at the end. Shall I put it into the operators section then or put it into the Punctuators
section but keep the current enum value? I've later changed the token to be just @@
only.
/// <summary> | ||
/// Gets or sets a value indicating whether inline splatting syntax (@@) was used, false otherwise. | ||
/// </summary> | ||
public bool Splatted { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than creating this as a new public property on HashtableAst
, I think it should be on ExpressionAst
, since splatting is intended to be generalised.
Also, AST types in PowerShell are almost always immutable, so there shouldn't be a setter ideally, unless it must be changed after the AST is constructed (this is usually the consequence of contextual information required by a grammar feature that PowerShell's LL(k) recursive descent parser can't deal with naturally)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since HashtableAst
inherits from ExpressionAst
, we can always change that later without a breaking change. I think we should only increase the areas where it is exposed on a need by need basis unless you can think of a case where the change in this PR could make it useful to have the property on ExpressionAst
as well.
bool splatted = false; | ||
if (atCurlyToken.Kind == TokenKind.AtAtCurly) | ||
{ | ||
NextToken(); // to skip the first '@' to allow the parsing of the hashtable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI SkipToken()
serves this purpose
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found that it did not work when using SkipToken
as it crashes some other logic in PowerShell just when typing @{{
bool splatted = false; | ||
if (atCurlyToken.Kind == TokenKind.AtAtCurly) | ||
{ | ||
NextToken(); // to skip the first '@' to allow the parsing of the hashtable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here where we've seen AtAtCurly
and we now skip a token, there's a problem because @@
might be followed by other tokens and we don't check. This causes, for example:
My recommendation is to restructure the tokens, which I've discussed in a comment in the tokeniser.
But also there are two important things to consider:
- An
@@{
token should only be valid as an argument to a command. This should be a semantic check. - Adding a new token, error checking and recovery is critical, since otherwise the grammar may have new pitfalls in it. The easiest solution here is to make
@@{
its own whole token.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I've changed it to perform a look-ahead so that @{{
is correctly parsed without ambiguity.
…o InlineSplatting_AtAtCurly
…o InlineSplatting_AtAtCurly
… InlineSplatting_AtAtCurly_2
…The following still produces a hashtable though (it should probably throw an error, which the RTM version does atm): @@{'a'='b'}
@rjmholt @lzybkr I've reacted to all the comments and implemented changes: I've changed it to perform a look-ahead so that it correctly recognises |
It should be said that both PRs do not implement all the features in the RFC, just a subset of it that are the most important ones for me and the other author, which I think is OK in terms of being minimum, viable. I'd be happy as long as either of the 2 PRs get accepted. When deciding between the 2 PRs, I'd like the @PowerShell/powershell-committee to also consider what is best in terms of follow up PRs if other people contribute e.g. the remaining features in the RFC or tab completion for the inline-splatted arguments. |
We in the @PowerShell/powershell-committee are going to holistically review this vs. #10984 next week in our RFC meeting. This one seems to match the RFC we approved (at least skimming through), while @KirkMunro's does not, but we want to also consider the That being said, we did review the original RFC0002 and its feedback very extensively before accepting. |
I like -splat, only because @@{ feels so visually jarring and so much like a typo. |
Personally, I don't like either very much. I don't have a compelling alternative, but I wish I did. |
I prefer It would be very strange to merge a PR that contradict an approved RFC without having another RFC to back it. 🤔 |
People on Twitter literally requested that we weigh in on what we’d prefer, so that’s what we’ve done. |
I prefer |
The one real criticism I have for |
I dislike @@{ for the same reason; this is starting to look very Perl-like; very mysterious and very un-PowerShell. My humble opinion is that -splat is the better way. (Throwing in my knee-jerk reaction after seeing this brought up on Twitter) |
An extra parameter, that magically affects other parameters is a lot harder to wrap my head around. It is also less clear to me in terms of precedence - i.e. what if you provide some parameters but also the I see the point of saying "explicit, spelled out is better than a symbol" but |
A very good point, and something my knee-jerk reaction hadn't considered. I still shudder at the proposed syntax. I get the point the you and Keith make about @ being the splatting operator already (so splatting a hashtable follows). I also imagine that the actual real-world use may mean that beginners aren't heavily impacted. I guess I'm just subliminally recalling those occasions when I opened a Perl script that I'd written and wondered if I'd been stoned at the time. It's the aesthetics rather than the basic lexicography. |
@jamesBru mentioned briefly during the weekly PowerLunch community chat a possibility that I like a little more; It's a nice medium between this implementation and |
I prefer |
I have to admit that However, I think my personal preference is for a splat sigil or operator:
> $x = 1,2,3
> $y = @{ d = 'a'; e = 'b' }
> $z = @{ f = 'Duck'; g = 'Banana' }
> function Test($a, $b, $c, $d, $e, $f, $g) { $a; $b; $c; $d; $e; $f; $g }
> Test @x @y @z
1
2
3
a
b
Duck
Banana |
@rjmholt In my opinion |
Yeah I get that, saw above you said it currently parses as a bareword literal. But my concerns are both more and less technical than that:
|
… InlineSplatting_AtAtCurly # Conflicts: # src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs # src/System.Management.Automation/engine/parser/token.cs # test/powershell/Language/Parser/Parsing.Tests.ps1
…ster/powershell into InlineSplatting_AtAtCurly
@rjmholt That wouldn't be an issue if -@ accepted an array, like the -splat PR already does (IIRC -- it's been a while). |
Also this discussion goes a little further than simply the syntactical differences between The latter also supports splatting properties or method results, such as: function Do-This {
[CmdletBinding()]
param(
[string]$Value
)
$sb = {
# $PSBoundParameters is empty in this child scope, but $PSCmdlet.MyInvocation.BoundParameters is not
Invoke-Something -splat $PSCmdlet.MyInvocation.BoundParameters
}
& $sb
} In general, the Also related, for splatting properties or method results, there is a separate PR (#11003) that supports doing that with the normal splat operator that we all use now (e.g. |
@PoshChan please retry macos |
@bergmeister, did not find matching build context: |
@PowerShell/powershell-committee discussed this in context of RFC0002 where we decided that we would withdraw RFC0002 with the exception of fixing the issue to allow explicit parameters to override a splatted hashtable and discuss named parameters for .NET method invocation as a separate issue. So in light of that, we will be closing this PR as discussed with @bergmeister |
no -splat, no -@, no @@ yet, will there be anything like that in the future? It seems like all approaches to improve splatting and thus usability are declined. Is there anything else in the work? |
PR Summary
This is the first feature of RFC002 for enhanced splatting.
As agreed in the accepted RFC, one should not need to declare a variable in order to splat. This PR adds the ability to definite the splatted hashtable in the same line as the command. As discussed, the
@@
has to be introduced to differentiate from the scenario where the 1st argument is a hashtable.The scenario that this PR enables is:
This PR adds the new
At
TokenKind
in order to be able the parse the syntax and adds a publicSplatted
property toHashTableAst
so that the compiler knows at runtime that the argument is splatted.The experimental feature
PSGeneralizedSplatting
is created for it. Although there are switch statements using the newAt
that are not guarded by an if check for this experimental feature (which was not possible due to the flow analysis of C# resulting in a compiler error), this is OK because the experimental feature flag prevents the tokenizer from creating anAt
token in the first place.A semantic check was added so that the only implemented use case is using
@@{
as an argument to a command, therefore the user will get an actionable error when trying to use it in another context, e.g. for a .Net method or a hashtable as@@{'key='value'}
to conform with previous behaviour (where the tokenizer returned an error in those cases). I've also checked it does not impact using@@
inside a function name (even before a function name could not start with@@
so no change here).This PR does not add tab completion functionality to the keys of the splatted hashtable. This is something that is not present at the moment anyway with splatted variables and is a separate enhancement that one could add in another PR, as well as other features in the RFC. The goal of this RFC is to implement just 1 feature in this RFC that adds value, therefore making it a minimum viable feature.
I added tests for existing and new splatting functionality and error cases.
PR Context
https://github.com/PowerShell/PowerShell-RFC/blob/master/2-Draft-Accepted/RFC0002-Generalized-Splatting.md
PR Checklist
.h
,.cpp
,.cs
,.ps1
and.psm1
files have the correct copyright headerWIP:
or[ WIP ]
to the beginning of the title (theWIP
bot will keep its status check atPending
while the prefix is present) and remove the prefix when the PR is ready.PSGeneralizedSplatting