Skip to content

RFC0002-Generalized-Splatting comments #6

Description

@lzybkr

Activity

  1. splatteredbits commented on Feb 26, 2016

    @splatteredbits

    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 -take and -slice operators, e.g.

    @{ 'fubar' = 'snafu' ; 'snafu' = 'fubar' } -take 'fubar'
    @{ 'fubar' = 'snafu' ; 'snafu' = 'fubar' } -slice 'fubar'
    

    Would return

    @{ 'fubar' = 'snafu' } # take
    @{ 'snafu' = 'fubar' } # slice
    

    It 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,0
    

    Would 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'
    
  2. KirkMunro commented on Feb 26, 2016

    @KirkMunro
    Contributor

    I 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.

  3. BladeFireLight commented on Feb 27, 2016

    @BladeFireLight

    Would the new splatting be usable with DSC resources?

  4. rkeithhill commented on Feb 29, 2016

    @rkeithhill

    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.

  5. DerpMcDerp commented on Mar 20, 2016

    @DerpMcDerp

    This 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:

    1. It's missing the ability to splat inside a hashtable literal, e.g. @{ a = 1 ; @$foo }
    2. 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.
    3. It's missing the ability to let you use barewords for keys in hashtable slicing. i.e. there is no way to unquote the bar in $hashtable + 'bar'.
    4. 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?

  6. DerpMcDerp commented on Mar 22, 2016

    @DerpMcDerp

    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.

  7. Jaykul commented on Jun 1, 2016

    @Jaykul
    Contributor

    Any chance we can splat objects, so that

    $foo | invoke-command

    is the same as

    invoke-command @?$foo
  8. Jaykul commented on Jun 1, 2016

    @Jaykul
    Contributor

    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 + $keys meaning 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 -slice or -take to mean _keep_ing things, and use -strip or -remove or something for removing...

  9. KevinMarquette commented on Aug 28, 2016

    @KevinMarquette

    I love the idea of having more ways to spat operations. Having the option to splat $PSBoundParameters in 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 -splat operator 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 | command
    

    This 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 if ValueFromPipelineByPropertyName was set for each property.

    The relaxed splat could be -rsplat or -relaxedsplat.

    I would also be ok if the -splat came before the expression instead just like how -not works

    # 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 | command
    

    More 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].

  10. rkeithhill commented on Aug 28, 2016

    @rkeithhill

    Kevin 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 $PSItem it 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. :-)

  11. Jaykul commented on Nov 18, 2016

    @Jaykul
    Contributor

    Side 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" }
    }
  12. felixfbecker commented on Sep 10, 2018

    @felixfbecker

    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-FooApiRequest function. If the API needs authentication (most APIs do), the functions will all commonly have to format something like a Token parameter. 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 a pick operation, which I would not expect from +.

  13. Jaykul commented on Oct 17, 2018

    @Jaykul
    Contributor

    What does the RFC process say about RFCs that sit in draft with no response to comments for 2 and half years?

  14. vexx32 commented on Oct 17, 2018

    @vexx32
    Contributor

    Jason 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 -slice operators (-splat being 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 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 _

    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.

  15. 34 remaining items

  16. Jaykul commented on Oct 19, 2018

    @Jaykul
    Contributor

    Yeah, the only reason I mentioned the Where method is that it seems like a simple implementation of the proposed -take and -slice in 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.

  17. vexx32 commented on Oct 19, 2018

    @vexx32
    Contributor

    Agreed. That seems to be the most elegant solution for avoiding potential mountains of unnecessary additional changes with this RFC.

  18. KirkMunro commented on Oct 19, 2018

    @KirkMunro
    Contributor

    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)
  19. vexx32 commented on Oct 19, 2018

    @vexx32
    Contributor

    "Can we" -- probably. I've been attempting to borrow from NamedAttributeArgumentAst to make that work. It's not been easy. But we probably can.

  20. Jaykul commented on Oct 19, 2018

    @Jaykul
    Contributor

    Kirk Munro (@KirkMunro) that's what I said. Totally agree. Just need to implement it 😉

  21. felixfbecker commented on Oct 19, 2018

    @felixfbecker

    Kirk Munro (@KirkMunro) that's not splatting, those are named parameters.

  22. vexx32 commented on Oct 19, 2018

    @vexx32
    Contributor

    Yep. Would be great to have those, but that's not for this RFC specifically, I think.

  23. Jaykul commented on Oct 19, 2018

    @Jaykul
    Contributor

    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 SERVER2
    
    

    We 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 to dotnet run or 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 )
    
  24. vexx32 commented on Oct 19, 2018

    @vexx32
    Contributor

    I 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
  25. SeeminglyScience commented on Jan 22, 2019

    @SeeminglyScience

    Not 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.

  26. SteveL-MSFT commented on Mar 18, 2019

    @SteveL-MSFT
    Member

    Closing this as this RFC is now Draft Accepted as #154

    Any new discussion should be opened as new issues

  27. iSazonov commented on Mar 19, 2019

    @iSazonov
    Contributor

    We could open new issue in PowerShell repo to track implementing the RFC (maybe with special label).

  28. anonhostpi commented on Feb 20, 2025

    @anonhostpi

    Is 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.

  29. iRon7 commented on May 27, 2025

    @iRon7

    Quote:

    I'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.

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

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions