Repository navigation
RFC0002-Generalized-Splatting comments #6
Description
Activity
I don't like using
+as the slice operator. The+operator means "take argument 1, combine it with argument 2, and return a new thing". In your examples, the meaning changes to "take argument 1, take parts of it out, and return a new thing". This is confusing and increases cognitive load: you have to remember this strange behavior.I suggest creating new
-takeand-sliceoperators, e.g.@{ 'fubar' = 'snafu' ; 'snafu' = 'fubar' } -take 'fubar' @{ 'fubar' = 'snafu' ; 'snafu' = 'fubar' } -slice 'fubar'Would return
@{ 'fubar' = 'snafu' } # take @{ 'snafu' = 'fubar' } # sliceIt could even be augmented to support arrays (not sure if it should take/slice by index or value):
@( 'a', 'b', 'c' ) -slice 1..2 @( 'd', 'e', 'f' ) -take 2,0Would return
@( 'a' ) @( 'f', 'd' )The example from the RFC would look like
Get-ChildItem @$($PSBoundParameters -slice 'Force') # Splat all parameters but 'Force' Get-ChildItem @$($PSBoundParameters -slice 'Force','WhatIf') # Splat all parameters but 'Force' and 'WhatIf' Get-ChildItem @$($PSBOundParameters -take 'LiteralPath','Path') # Only splat 'LiteralPath' and 'Path'Reacted by Felix Becker and Johan LjunggrenI love that you're moving forward on improvements to splatting! I'll use the new splatting features heavily once they are implemented/released!
Now, some feedback about the RFC:
In the Specification section, when you say "which means splatting without the second '@' would be a breaking change", do you mean to say "if we were to allow inline splatting of a hashtable without the second '@', that would be a breaking change, so two '@''s are required for this to work"?
When we use splatting today, we can splat a variable that contains a hashtable without providing the leading '$' to indicate that it is a variable (in fact, that's how we have to do it because it's the only way it works). With the proposed improvements, at the end of the Specification section, you show the ability to use inline subexpressions to build the hashtable that will be splatted into the command (e.g. Get-ChildItem @$(Get-ChildItemArg)), and you also show splatting in a method that returns a hashtable. Both of these techniques use subexpressions. I understand that the leading '$' is required for subexpressions because otherwise you would be using array enclosures which would not be splattable, so like the previous point, you must use @$(...) when splatting in a subexpression. I hope, however, that you also allow for direct splatting of a property or of the results of a method without the leading '$' character, which is inline with how splatting works today (no '$' character is required). i.e. I would like to see this specification extended to support the following two scenarios:
# Splat a hashtable that is inside a property of an object Invoke-Something @pscmdlet.MyInvocation.BoundParameters # Splat a hashtable that is returned from a method Invoke-Something @obj.GetInvokerArgs()
I don't think either of these being supported is an unreasonable request, because they would offer a more natural extension to the splatting that we have today.
For relaxed splatting, and all other sections in the remainder of the document, I'd prefer if the '$' symbol was only necessary when using subexpressions, as mentioned above. Please consider keeping the '$' supported but optional when you are splatting a variable (or a property of that variable or the results of a method on that variable).
For Splatting in switch cases, I personally find that one odd for splatting, and wish the splat prefix simply wasn't necessary. Since you can't pass an array into a switch statement as an array without it unwrapping unless you prefix it with a comma, not having to splat here would be greatly preferred (but I know, someone out there could be passing in an array to a switch statement with the ',' prefix so that it is compared as an array, and they could be using a statically defined array in their comparison, so it could be a breaking change. Regardless of those wishes, splatting seems awkward in this scenario, to me at least.
For Modifying hashtables for splatting, I prefer the alternate proposal suggestion of other operators, and wonder what operators you have in mind for that. +/- seems too confusing to me.
I'm really looking forward to this proposal coming to light, and hope these comments help move forward on this design.
Would the new splatting be usable with DSC resources?
I like the proposed changes from a perspective of nice enhancements to splatting but when I see this particular scenario:
Update-TypeData @@{ TypeName = 'MyCustomType' MemberName = 'MyCustomProperty' MemberType = 'NoteProperty' Value = 42 }
I can't help but wonder if we had a more reliable line continuation char (where it didn't matter if there was whitespace after it), if this particular scenario wouldn't be simpler as:
Update: realized that
_can be a valid command name. Sigh... back to backtick for now.Update-TypeData ` -TypeName MyCustomType ` -MemberName MyCustomProperty ` -MemberType NoteProperty ` -Value 42
This way, I don't have to quote every argument. It is just passing parameters/args as normal.
Reacted by Joel BennettReacted by Joel BennettThis proposal doesn't address all the desirable features in "Here is what a proper splatting proposal needs to do" on Allow splatting without an intermediate variable. Specifically:
- It's missing the ability to splat inside a hashtable literal, e.g.
@{ a = 1 ; @$foo } - It's missing the ability to specify lazily evaluated default values for hashtable slicing if keys are missing. The
$hashtable + 'LiteralPath',@{Force = $true}syntax could be made to work to allow for missing values but unfortunately it wouldn't be lazily evaluated. Also, this slicing syntax couldn't work for array slicing since$array + 1,@{3 = $true}already means array concatenation. - It's missing the ability to let you use barewords for keys in hashtable slicing. i.e. there is no way to unquote the
barin$hashtable + 'bar'. - This proposal only talks about splatting hashtables to methods but what about splatting arrays to methods?
Also, this RFC weirdly has the restriction:
# Error, multiple splatted arguments $str.SubString(@@{startIndex = 2}, @@{length=2})
Why should that be an error?
# Error, splatted argument is not last $str.SubString(@@{length=2}, 2)
But what if the splatted argument is an array? Is it still required to be last?
Reacted by Keith HillReacted by Kirk Munro- It's missing the ability to splat inside a hashtable literal, e.g.
If splatting inside hashtable literals is allowed, relaxed splatting with
@?should be made to work inside hashtable literals too, e.g.$a = @{ A = 1 @?@{ A = 2 B = 3 } } # $a -eq @{ A = 2 ; B = 3}
To let us work around PowerShell's "duplicate keys are not allowed in hashtable literals" error.
Reacted by Kirk Munro and Joel BennettAny chance we can splat objects, so that
$foo | invoke-command
is the same as
invoke-command @?$foo
A slice operator as suggested by Aaron Jensen (@splatteredbits) is interesting (I think the semantics of -slice are wrong in that proposal -- usually when we slice an array we specify what to keep?). Maybe if we got a slice operator, PowerShell could get array slicing the way Python does it instead of the way it works now -- so that it's faster, and can increment in steps?
That is, I agree with Aaron Jensen (@splatteredbits) that the RFC proposal for
$hash + $keysmeaning the same things as...$hash - ($hash.keys -ne $keys)is really not intuitive, but I think you need different names for the proposed operators. Perhaps use-sliceor-taketo mean _keep_ing things, and use-stripor-removeor something for removing...KevinMarquette commented
on Aug 28, 2016 More actionsI love the idea of having more ways to spat operations. Having the option to splat
$PSBoundParametersin a proxy function without removing some is currently lacking and would be great to have. That is such a common pattern that we should have a way to address it.My only concern about using
@for all splat operations is that all those examples did not feel all that intuitive for people getting into PowerShell. I think it kind of makes the code less readable to them. Splatting already blows their mind. I would almost prefer something slightly more verbose that would be searchable.What about the addition of a
-splatoperator and a[PSCustomSplatObject]. It would be very searchable and discoverable to a larger audience.# Equivalent - but splatting an expression (the value of a variable) command ($PSBoundParameters -splat) # Splat another expression - a hashtable Update-TypeData (@{ TypeName = 'MyCustomType' MemberName = 'MyCustomProperty' MemberType = 'NoteProperty' Value = 42 } -splat )I would like it even better if it is something that can be pipped.
$PSBoundParameters -splat | commandThis just feels more PowerShell. It would have to produce a new
[PSCustomSplatObject]that the interpreter would identify and handle in a special way. It would act as ifValueFromPipelineByPropertyNamewas set for each property.The relaxed splat could be
-rsplator-relaxedsplat.I would also be ok if the
-splatcame before the expression instead just like how-notworks# Equivalent - but splatting an expression (the value of a variable) command (-splat $PSBoundParameters) # Splat another expression - a hashtable Update-TypeData ( -splat @{ TypeName = 'MyCustomType' MemberName = 'MyCustomProperty' MemberType = 'NoteProperty' Value = 42 })Treating the splat as a special
[PSCustomSplatObject]object would introduce some unique opportunities for other advanced operations.$options = $PSBoundParameters -splat command $options $options | commandMore importantly, this is something that can be returned from a function.
#sample function function Get-Options { [PSCustomSplatObject]@{startIndex = 2} } # Get-Output returns a splatted object command (Get-Options) # Splat a hashtable defined outside the method call $subStringArgs = @{startIndex = 2} -splat $str.SubString($subStringArgs) # Splat a hashtable defined inline in the method call $str.SubString(@{startIndex = 2; length=2} -splat) # Splat a function return object that is pre-splatted inline in the method call $str.SubString(Get-Options)This new splat object would act like a hashtable in every other way. It could just be a hashtable with a different object type name. We could call it a
[PSCustomSplatObject]and even allow casting and creating hashtables with that type.# Splat another expression - a hashtable Update-TypeData [PSCustomSplatObject]@{ TypeName = 'MyCustomType' MemberName = 'MyCustomProperty' MemberType = 'NoteProperty' Value = 42 }Then we could alias
@to the[PSCustomSplatObject]. Then this would align nicely with the RFC. With the exception that best practices and script analyzer would recommend[PSCustomSplatObject].Reacted by Nicholas M. GetchellReacted by Joel Bennett and Bartek BielawskiKevin Marquette (@KevinMarquette) I don't know. Splatting is not required so folks don't have to use it. And you can't take out the current splatting mechanism so new folks are going to see the old style in scripts all over the place. I guess I don't see much value in introducing an alternate splatting mechanism. In fact, having multiple implementation just muddy the waters. That said, PowerShell excels at having multiple ways to do things. :-)
Whenever, I see
$PSItemit bums me out a little. You don't see it much because most folks use$_but when I do run across I find myself doing a double-take. :-)Reacted by Joel BennettSide note regarding Kirk Munro (@KirkMunro)'s comment about Splatting in switch cases*:
I personally find that one odd for splatting, and wish the splat prefix simply wasn't necessary. Since you can't pass an array into a switch statement as an array without it unwrapping unless you prefix it with a comma, not having to splat here would be greatly preferred (but I know, someone out there could be passing in an array to a switch statement with the ',' prefix so that it is compared as an array, and they could be using a statically defined array in their comparison, so it could be a breaking change. Regardless of those wishes, splatting seems awkward in this scenario, to me at least.
Since array comparisons don't work in PowerShell (i.e. two arrays are never equal unless they're actually the same reference), I don't think the "but I know" part of this statement is true. There's really no reason we would need to use splatting inside a switch to define an array of test cases which all have the same body. You could just change the switch statement clause to allow arrays as the "case" :
$Many = "A","B","C","D" switch($Many) { "A" { "Primary" } "B","C" { "Secondary" } default { "Other" } }
In particular, I really like the "ignore keys in the splatted hashtable that are not parameters" part of this RFC. The current behaviour is extremely annoying when
- Writing generic ArgumentCompleters
- Implementing functions that always need to forward certain parameters, for example an API client with functions for every action+resource, that all internally call to a generic
Invoke-FooApiRequestfunction. If the API needs authentication (most APIs do), the functions will all commonly have to format something like aTokenparameter. It would be nice if it could just splat its own parameters, but that currently doesn't work because PowerShell will error on the unknown parameters.
What I don't like (as mentioned in this thread) is the new behaviour of
+and-to hashtables. I would expect them to work like it does with arrays, i.e. a+of two hashtables returns the union, and-the difference. But the way it's outlined in the proposal:Get-ChildItem @$($PSBOundParameters + 'LiteralPath','Path') # Only splat 'LiteralPath' and 'Path'
+acts like what is referred to in other languages as apickoperation, which I would not expect from+.What does the RFC process say about RFCs that sit in draft with no response to comments for 2 and half years?
Reacted by Rain Sallow (/u/ta11ow) and Flavien MICHALECZEKvexx32 commented
on Oct 17, 2018 ContributorMore actionsJason Shirk (@lzybkr) Joel Bennett (@Jaykul) Ilya (@iSazonov)
My personal comments, having read through the RFC:- While I very much dislike how syntactically noisy the
@@{Param = Value}syntax is, I'm not sure there is a good way to avoid it and still implement this in a useful fashion.- I do like the above ideas of
-splat,-take, and-sliceoperators (-splatbeing a unary would be awesome, I think) but am unsure exactly how that implementation would function given that the common use cases for a splat are not in a context where a typical operator can be used.
- I do like the above ideas of
- I also acknowledge that making this an operator in full would significantly complicate the syntax, as the two most common places you want to splat something are following a command or inside parentheses for a method invocation -- neither of which typically accept operators without additional parentheses.
- I almost want to suggest a specialised syntax here for the express purpose, maybe something like
@[$splatExpression]although that has its own drawbacks and could conceivably be used for something else. - On that thought, perhaps a different prefix needs to be considered, though we frankly have few options. The only ones I can think of that aren't currently used in things that might clash here are
&,^,<,~and perhaps_
- I almost want to suggest a specialised syntax here for the express purpose, maybe something like
It seems the implementation of the different tokenizer modes almost backs us into a corner in this way, where permitting odd syntax like
@$(expression)becomes a necessity. Arcane syntax has never really been a core tenet of PowerShell, and I'm not particularly a fan of the cognitive-heavy syntax here. As a result, I am unsure if it truly makes sense to attempt to unify splatting with something like named method parameters. I almost want to suggest we not do this and try to come up with something more effective and adaptable in terms of syntax before this is attempted.Throwing a suggestion or two out there that I think would be a little less cluttered in terms of the syntax:
Invoke-Thing @:{ Parameter = 'value' Count = 4 } $Object.Method(@:{ Parameter = Value Param2 = 3 }) Splat-Expression @:(Get-Things) $Splat = @{Path = $home} Get-ChildItem @:Path
I favor this because it's less visually muddy and cluttered, while still being completely distinguishable from the standard hashtable or array subexpression syntax.
- While I very much dislike how syntactically noisy the
34 remaining items
Yeah, the only reason I mentioned the
Wheremethod is that it seems like a simple implementation of the proposed-takeand-slicein a semantic way, without introducing any new concepts or operators. If we just say we're going to do it, we can just decide not to do anything else here.Reacted by Rain Sallow (/u/ta11ow)vexx32 commented
on Oct 19, 2018 ContributorMore actionsAgreed. That seems to be the most elegant solution for avoiding potential mountains of unnecessary additional changes with this RFC.
Regarding inline assignment of method arguments by name, do we need to use hashtables for that? Or can we simply follow the C# model?
e.g.
$obj.Method($arg1, optionalArgName1: "argValue", optionalArgName2: 42, anotherOptionalArg: $foo)
vexx32 commented
on Oct 19, 2018 ContributorMore actions"Can we" -- probably. I've been attempting to borrow from
NamedAttributeArgumentAstto make that work. It's not been easy. But we probably can.Kirk Munro (@KirkMunro) that's what I said. Totally agree. Just need to implement it 😉
Kirk Munro (@KirkMunro) that's not splatting, those are named parameters.
vexx32 commented
on Oct 19, 2018 ContributorMore actionsYep. Would be great to have those, but that's not for this RFC specifically, I think.
Felix Becker (@felixfbecker) yeah, it's a good point -- when people talk about "inline splatting" it's not splatting anymore -- it's just a different syntax for parameters.
- If we add .Where for dictionaries instead of automatically relaxed splatting ...
- If we allow splatting properties and method calls like
@var.where()...
Then we have a syntax we can agree on for everything except inline splatting, right?
What if we stop trying to make "inline splatting" a thing, and have a separate RFC about "better syntax for passing dozens of parameters to a function" -- how about one of these:
Here parameters. Like a here string, but for parameters:
Get-WmiObject @[ -Class Win32_LogicalDisk -Filter "DriveType=3" -ComputerName SERVER2 ]A parameter continuation character.
Unlike
--%this one would mean that all lines until a blank one are parameters:Get-WmiObject --- -Class Win32_LogicalDisk -Filter "DriveType=3" -ComputerName SERVER2We could use
--@or just--or--_or even--...it doesn't really matter. As long as it's followed by (optional whitespace and) a new line, it wouldn't run into anything like the--parameter todotnet runor anything.You know Rain Sallow (/u/ta11ow) (@vexx32), that might not be a bad solution for your named parameters too. What if instead of trying to shoehorn names into the parameter block, we flip the idea on it's head and decide to support calling methods the way we call functions? Something like one of these?
$str.SubString -startIndex 3 -length:$count $str.SubString() -startIndex 3 -length:$count $str.SubString( -startIndex 3 -length:$count )vexx32 commented
on Oct 19, 2018 ContributorMore actionsI would be a fan of the first one I think. Would be nice to do that.
Also, it might enable using "stored methods" for that kind of thing (whatever that's called):
$str.Substring -StartIndex 3 -Length $count $Method = $str.Substring & $Method -StartIndex 3 -Length $count
SeeminglyScience commented
on Jan 22, 2019 More actionsNot sure if discussion is still open on this, but another syntax option that I don't believe has been brought up is
<. Currently<breaks most parsing modes, so the amount of symbols required is pretty flexible. It also sort of fits thematically.Here's some examples/options
# Redirect a normal hashtable Get-ChildItem < @{ Path = '\' } # Require explicit syntax Get-ChildItem <{ Path = '\' } # Method splatting $a.Foo<{ arg1 = 'value' } $a.Foo('value1', <{ arg2 = 'value2' })
All of the above scenarios fail to parse with
The '<' operator is reserved for future use.. The syntax<{ }could also be enforced to allow stdin redirection to be implemented in the future.Reacted by Rain Sallow (/u/ta11ow)Reacted by Rain Sallow (/u/ta11ow) and Joel BennettReacted by Rain Sallow (/u/ta11ow)Closing this as this RFC is now Draft Accepted as #154
Any new discussion should be opened as new issues
Reacted by Rain Sallow (/u/ta11ow)We could open new issue in PowerShell repo to track implementing the RFC (maybe with special label).
Reacted by Rob Holt and norachugaIs there any process for requesting the RFC to be revisited or reopened? I think there's a fundamental difference between disagreeing on implementation and finding an implementation useful.
My understanding is that this RFC stagnated, because there was a drawn-out conflict over which implementation should be used (
@@...or-splat @...).I'm in favor of the
@@...syntax, and have disagreements with the-splat @...syntax, but I would just be happy to just have any implementation at all.I think there's a point when you have to recognize that community-driven progress can be a trainwreck and isn't very driving at all.
In circumstances like this, I would be much happier with an executive decision on what method gets used over no decision at all.
Reacted by Joel BennettI'm attempting to take another stab at splatting enhancements in PowerShell and have opened a summary of the various options proposed over the years and am hoping to get feedback from anyone interested. If you are interested in having a look and/or wanting to comment on some of the options I would love any feedback at jborean93#1.
The aim is to get more community consensus around the various options before settling on a real RFC based on the feedback and going from there.
Reacted by Michael Klement
Use this issue to comment on this RFC: https://github.com/PowerShell/PowerShell-Language-RFC/blob/master/1-Draft/RFC0002-Generalized-Splatting.md