GithubHelp home page GithubHelp logo

Comments (60)

lzybkr avatar lzybkr commented on June 16, 2024 5

This discussion is interesting but I'm afraid it's headed into bike shedding territory.

@ already means splat (and somewhat unfortunately other things). + and * don't mean splat and they also already have significant meaning.

I don't think anyone would reasonably suggest adding |> to mean | even if it looks nicer and was purely additive to |.

from powershell-rfc.

Jaykul avatar Jaykul commented on June 16, 2024 4

@vexx32 Uhm, ... $str.Substring(+2) and Get-Random +42 come to mind ...

from powershell-rfc.

Jaykul avatar Jaykul commented on June 16, 2024 4

Honestly, of all these suggestions, the only one that seems cleaner to me than @@{ and still keeps the somewhat intuitive relationship to @ for splatting is the @[ syntax @vexx32 mentioned. It's not obviously a hashtable, but if you see the code, you're going to be able to guess what's happening -- and for what it's worth @[ is going to be an error currently: Unrecognized token in source text.

Get-WmiObject @[
    Class = "Win32_LogicalDisk"
    Filter = "DriveType=3"
    ComputerName = "SERVER2"
]

Obviously this is only meant for "inline" splatting. As far as splatting variables and properties, I agree with Kirk that we don't need multiple characters, but I also don't see why we'd need to change it -- I think @var.property or @var.method() would be fine.

For what it's worth (because everyone else is offering alternatives), I actually like the idea of using : instead of = for named parameters. That's what we use in C# and PowerShell (although powershell sometimes supports just a space, for switches you_require_ -Debug:$false) ...

Given that, we could write it like this:

Get-WmiObject @[ Class: "Win32_LogicalDisk"
                 Filter: "DriveType=3"
                 ComputerName: "SERVER2" ]

And with that syntax, I don't think you need the @[ in a method call. If you're using : this might work:

"hello".Substring(startIndex:3, length:$count)

from powershell-rfc.

KevinMarquette avatar KevinMarquette commented on June 16, 2024 3

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

from powershell-rfc.

lzybkr avatar lzybkr commented on June 16, 2024 3

The important part of my proposal is the consistency - if you understand @ to mean - splat something, it mostly works for whatever comes next.

My proposal is also motivated in part by splatting in Ruby, lots of good references like here.

I thought I even took the switch example from Ruby, but I'm not sure now. I honestly don't have a strong opinion about the support in switch though.

I do like the suggestion from @KirkMunro to also support members this way:

Invoke-Something @pscmdlet.MyInvocation.BoundParameters
# Equivalent to
Invoke-Something @$pscmdlet.MyInvocation.BoundParameters

from powershell-rfc.

SeeminglyScience avatar SeeminglyScience commented on June 16, 2024 3

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.

from powershell-rfc.

rkeithhill avatar rkeithhill commented on June 16, 2024 2

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.

from powershell-rfc.

DerpMcDerp avatar DerpMcDerp commented on June 16, 2024 2

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?

from powershell-rfc.

DerpMcDerp avatar DerpMcDerp commented on June 16, 2024 2

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.

from powershell-rfc.

Jaykul avatar Jaykul commented on June 16, 2024 2

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

from powershell-rfc.

KirkMunro avatar KirkMunro commented on June 16, 2024 2

I can't say I'm a fan of @@ or @$ at all... feels like obscure syntax that is forced in rather than elegant syntax that is planned and designed, and results in way too many symbols. This is especially true when you end up with something like @$($foo....). It's just not natural. I get why it has been proposed -- people already use @ for splatting, and those build on that foundation. But what if @ was maintained for backward compatibility with simple splatting that is there today, and something else was added that could be used for simple splatting plus all of these other scenarios in a more elegant manner?

Some examples, modified from the original RFC and some of the comments:

# Existing usage, maintained for backwards compatibility:
command @PSBoundParameters

# New usage:
command +PSBoundParameters

# New usage (splatting a member of an object)
command +PSCmdlet.MyInvocation.BoundParameters

# New usage (splatting the results of a method)
command +obj.GetCommandArgs()

# Inline splat of a bunch of parameters
Update-TypeData +{
    TypeName = 'MyCustomType'
    MemberName = 'MyCustomProperty'
    MemberType = 'NoteProperty'
    Value = 42
}

# Inline splat an array
ls +(
    'test'
    'test2'
)

# Call a command that returns a hashtable or array to splat
Get-ChildItem +Get-ChildItemArgs

# Relaxed splatting (just use ? to indicate you want to apply the values
# but only if there are matches -- only matters for dictionaries)
$myArgs = @{Path = $pwd; ExtraStuff = 1234}
Get-ChildItem ?myArgs

# Splatting in method invocations
$subStringArgs = @{startIndex = 2}
$str.Substring(+subStringArgs)

# Inline works too
$str.Substring(+{startIndex = 2; length = 2})

# Mixing splatting and positional arguments
$str.SubString(2, +{length=2})

# Must be an error, parse time or runtime, because startIndex
# is specified positionally and via splatting.
$subStringArgs = @{startIndex = 2}
$str.SubString(2, +subStringArgs)

# Unlike the RFC, multiple splatted arguments would be allowed, as long as
# there were no conflicts (unless it's too difficult for the parser, why not?)
$str.SubString(+{startIndex = 2}, +{length=2})

# But like the RFC, splatted arguments must be at the end
# Error, splatted argument is not last
$str.SubString(+{length=2}, 2)

For the remaining scenarios proposed in the RFC, I have the following comments:

Switch cases

I believe the switch case needs would be much better covered without splatting, and feel that they should be left out of this RFC entirely.

Modifying hashtables

Modifying hashtables is something that could be useful more broadly than splatting -- an easy way to create a new hashtable that does not contain all of the values in the original one. Ditto for arrays. We already have support for drawing specific indices from arrays, but we don't have support for drawing specific values from hashtables. The comment that proposed -slice and -take could handle that need, as could extension methods on dictionaries. If we had extension methods, maybe we could do this:

$ht = @{
    OneFish = 1
    TwoFish = 2
    RedFish = 'Red'
    BlueFish = 'Blue'
}

# Splat in only OneFish and RedFish
command +ht.Take('OneFish','RedFish')

# Splat in all but TwoFish
command +ht.Leave('TwoFish')

This leaves the proposed syntax for splatting intact while providing more value to dictionaries in general.

More food for thought. 🍕

from powershell-rfc.

lzybkr avatar lzybkr commented on June 16, 2024 2

To be honest, @ is more suggestive of splat, as in "flatten on impact" than pretty much any other single character.

from powershell-rfc.

iSazonov avatar iSazonov commented on June 16, 2024 2

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

from powershell-rfc.

splatteredbits avatar splatteredbits commented on June 16, 2024 1

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'

from powershell-rfc.

rkeithhill avatar rkeithhill commented on June 16, 2024 1

@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. :-)

from powershell-rfc.

Jaykul avatar Jaykul commented on June 16, 2024 1

Why are you putting your path separator in my hashtable? 😉

from powershell-rfc.

lzybkr avatar lzybkr commented on June 16, 2024 1

I'm a big fan of being more restrictive to start with, relaxing things only when it's obvious that the restrictions are unnecessary.

As things are today, your first example is not an error, but your second is:

$v1 = $value -eq 9
Get-ChildItem @v1
$v2 = @{Name = 'Steve'; Age = '32 }
@v2

I think this behavior is reasonable - splatting a non-array value isn't horrible, it's effectively the same as splatting a single element array.

As a pure expression - I think it's reasonable to have the error like today, I wouldn't change that.

from powershell-rfc.

KirkMunro avatar KirkMunro commented on June 16, 2024 1

@lzybkr I like the suggestiveness of @, but with @ used for arrays and hashtables already, both of which are the source of content splatted into commands, it's just messy. It worked well for the simple scenario that we have now, but it doesn't feel like a good fit for the rest of this IMHO.

I think +, or *, or whatever other single character symbol feels right, ideally while removing the need to follow it with $ or @ would make this much better. My preference is still for +...use - for explicit named parameters, + for parameters/arguments added from collections, and neither for positional parameters.

from powershell-rfc.

rkeithhill avatar rkeithhill commented on June 16, 2024 1

We've been using @ for years for splatting. It doesn't make any sense to me to switch to another sigil/character for splatting.

from powershell-rfc.

Jaykul avatar Jaykul commented on June 16, 2024 1

Oh, about filtering -- what if we just add a working implementation of .Where for hashtables (or rather, for IDictionary)? It could get only the keys passed in, or you could pass the whole KeyValuePair -- I've actually done this as an extension method in one of my modules at work, even though it's a bit inefficient this way:

Update-TypeData -TypeName Hashtable -MemberType ScriptMethod -MemberName Where -Force -Value {
    param([ScriptBlock]$Filter, $Count=0)
    [hashtable][Linq.Enumerable]::ToDictionary(
        [Collections.Generic.IEnumerable[PSObject]]($this.GetEnumerator().Where($Filter, $Count)),
        [Func[PSObject, PSObject]] {$args[0].Key},
        [Func[PSObject, PSObject]] {$args[0].Value})
}

Then you can do something like this:

@{ one = "uno"; two = "dos" <# etc. #> }.where{$_.Key -in "one","two"}

from powershell-rfc.

Jaykul avatar Jaykul commented on June 16, 2024 1

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.

from powershell-rfc.

SteveL-MSFT avatar SteveL-MSFT commented on June 16, 2024 1

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

Any new discussion should be opened as new issues

from powershell-rfc.

KirkMunro avatar KirkMunro commented on June 16, 2024

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.

from powershell-rfc.

BladeFireLight avatar BladeFireLight commented on June 16, 2024

Would the new splatting be usable with DSC resources?

from powershell-rfc.

Jaykul avatar Jaykul commented on June 16, 2024

Any chance we can splat objects, so that

$foo | invoke-command

is the same as

invoke-command @?$foo

from powershell-rfc.

Jaykul avatar Jaykul commented on June 16, 2024

A slice operator as suggested by @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 @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...

from powershell-rfc.

Jaykul avatar Jaykul commented on June 16, 2024

Side note regarding @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" }
}

from powershell-rfc.

felixfbecker avatar felixfbecker commented on June 16, 2024

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

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

@lzybkr @Jaykul @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.

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

Hehehe. Doubtful it can be confused as such in these contexts 😂

from powershell-rfc.

iSazonov avatar iSazonov commented on June 16, 2024

We already have special args entity and we could reuse the name like:

Invoke-Thing @args(@{
    Parameter = 'value'
    Count = 4
})

$Object.Method(@args(@{
    Parameter = Value
    Param2 = 3
}))

Splat-Expression @args(@(Get-Things))

$Splat = @{Path = $home}
Get-ChildItem @args($Splat)

This allow extended form like @Args(@{ … }, additional-parameters-and-modificators).
My thoughts are more from the parser side. :-)


Also we could silently ignore extra values in splat hash (why we need remove it?) and take in account method parameters having default values (scenario: splat hash has less values then the method parameter count).

from powershell-rfc.

felixfbecker avatar felixfbecker commented on June 16, 2024

I don't have a problem with @@{ key = 'value' }. It makes sense, and an inline hashtable will be rare anyway (why not just pass the parameters directly?). The case we should focus on is splatting a variable of a hashtable.
@$(expression) also makes sense to me (and is a rare case too).
I would rather have @@{} and @$() than change the already-existing sigil for splatting.

@args(...) seems confusing to me too. @args would splat the variable $args, so @args(...) to me looks like $args was a function that is being called, but I would expect a variable named $args to be a hashtable or array (in any language).

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

@felixfbecker I don't think a direct hashtable splat will be particularly rare.

Get-ChildItem @@{
    Path = 'C:\Windows'
    Filter = '*.dll'
    Recurse = $true
}

But yes, it's not as odd-looking as an inline one, which at least in theory should be much less common. 😄

I'm on the same page with you with @args( ) as well; it combines too many disparate syntaxes and just comes out confusing.

I can see there's some value in keeping the existing splat sigil, so as much as I'd probably prefer to use something else it might be better to keep it and use it as-is.


I turn my concerns then to method invocation. I don't think this syntax really works that well for method invocation, at least if we retain existing syntaxes as such:

$Object.Method(@@{
    Param1 = 'Value'
    Param2 = 12
    Param3 = $true
})

This feels very cluttered, and we've definitely got a redundant set of parentheses in these cases. Is it worth having the parser treat this such that you'd write it in this fashion instead?

$Object.Method@@{
    Param1 = 'Value'
    Param2 = 12
    Param3 = $true
}

This would effectively forbid @ as a bare, unescaped character in a method name, at least, and might potentially require excluding it from property names as a valid bare character also. I don't think that would be a huge concern (I don't think it's a valid character in C# method names either, anyway, and it's not a valid variable name character either). If you really needed to use it for a method or property name, you could just place the string sequence in quotation marks regardless.

That said, it also opens up less-than-clear syntax like $Object.Method@Args which... isn't super nice either.

I do like the 'relaxed splatting' option as well. Suggested syntax is good.


I'm.. not sure I like the 'splatting for a switch' idea, though. I think that idea is good, but I don't think splatting is the answer there & would be an unnecessary over-reach. Perhaps switches should simply permit 'case stacking' like C# already does (but with a different syntax):

switch (var)
{
    case 'a':
    case 'b':
        // do stuff
        break;
}

Suggested for PS, perhaps...:

switch ($var) {
    'a', 'b', 'c' {
        # do stuff
        continue
    }
}

from powershell-rfc.

felixfbecker avatar felixfbecker commented on June 16, 2024

I don't think a direct hashtable splat will be particularly rare.

You didn't provide any argument why. In your example, why would one not simply provide the parameters directly?

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

For the same reason one splats anything in the first place.

This can get quite lengthy:

Get-ChildItem -Path "\\Overly_Verbose_Server_Name\MyShare\Wow!\Why do people\have such long folder paths\it usually is not necessary\but here we are" -Include '*.pdf', '*.doc*', '*.xl*' -Recurse -Depth 3

And using backticks for that is a fragile alternative. Ultimately the reason splatting is used at all is really a reason you'd prefer a hashtable splat there.

Currently to splat the above for greater readability, you need to define parameters before you define the argument -- which, personally, I think is fine, but I do know some folks take issue with it because it risks semantically separating the parameters from the command to the point of it being very unclear at points why this hashtable is being defined.

Concerns for such things tend to increase along with the length and complexity of the script or function, and if you have many commands to splat... well. 😄

As a result, I can definitely see folks wanting to be able to splat a hashtable directly, without having to store it first.

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

Agreed on the member support. I also agree that consistency does need to be the name of the game here.

My only remaining question is -- what is "expected behaviour" for a splatted expression that doesn't contain a) an array or b) a hashtable?

And is a splatted expression any kind of meaningful value or collection of values on its own? Currently it errors out if you attempt to use it as anything other than a command value, but given that part of the this discussion is about supporting splatted expressions as method parameters, will that change?

In other words:

# is this valid? what would be a reasonable behaviour, or error message if not?
PS> Get-ChildItem @$($value -eq 9)

# or is this meaningful in any fashion?
PS> @@{ Name = 'Steve'; Age = '32' }

Oh, and one final point of interest -- is it worth extending splat logic to allow splatting from non-hashtable dictionaries? Naturally, there are still limitations that need to be there for sensibility (e.g., restricting it to dictionaries with string keys seems fairly sensible) but beyond that I don't see a particular reason to prevent anything that implements IDictionary from being inherently invalid in a splatted expression.

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

That seems reasonable. 😄

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

I like all of these ideas, but I am very wary of using + or ? as sole operators here. They have a purpose as an operator, and while this usage doesn't conflict with the usage in their normal modes, I don't think adopting an operator with an established usage is better than the semantically noisy @@ / @$

Agreed on pretty much all your other points though.

Looking at @lzybkr's link to Ruby docs above, we could adopt * instead, but realistically that has the same issues as + in this usage.

But... ultimately I agree, the suggested syntax is incredibly noisy and I would prefer alternatives.

Regarding allowing multiple splats, I believe command splatting already works with this. It would be strange not to permit it in the same way.

from powershell-rfc.

Jaykul avatar Jaykul commented on June 16, 2024

I gotta say, in answer to @felixfbecker yes, splatting hashtables will be common -- splatting is commonly recommended as one option to avoid long lines, and the current recommendation is to define the hashtable immediately above the line, but if this went through would presumably change to putting it inline, as @vexx32 said. To use the book's (bad, because it's too short to need this) example:

Get-WmiObject @@{
    Class = "Win32_LogicalDisk"
    Filter = "DriveType=3"
    ComputerName = "SERVER2"
}

However, @vexx32 I think it's important not to convince yourself that line shortening is the main reason people use splatting...

  1. It's a huge time-saver when passing on the parameters you got to a helper function.
  2. It's a very convenient and self-documenting way of collecting parameter values together when you need to run a bunch of commands and then pass all their values to another command as parameters.
  3. it's really useful when you need to read parameter values from configuration, etc.
    ... etc.

@KirkMunro I really agree with you and @vexx32 that it would be nice to have a more beautiful syntax, but + is a unary operator already ... and is legal in command names, and ? is a built-in alias for where).

function +foo { "Why, dear ones, why!?" }
function +2foo { "Oh my goodness, who's thought we'd want to do that?" }
+2foo

I'm not saying anyone has ever written anything so ridiculous 😉 but, you know ... you're treading very close to syntax that actually already works for other purposes. The real problem is that PowerShell has basically used every stinking character that's typeable, for something ... 😉

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

@Jaykul you're forgetting that these splat pseudo-operators would be only usable in contexts where command names and standard operators can't be used.

But yes, I agree we should avoid using symbols that carry other meanings if possible.

from powershell-rfc.

KirkMunro avatar KirkMunro commented on June 16, 2024

Keep in mind, that + means a lot of things. Generally speaking though, it means add. Add something to something else. And in the case of splatting, we're doing exactly that: adding arguments or parameters to a command invocation. With that in mind, I find the proposed syntax not conflicting, self explanatory and very clear, without conflict.

Speaking of conflicts, you can have filenames with @ in them (e.g. New-Item '@myhashtable'). That causes some issues with splatting (Get-Item @myhashtable). I just wanted to point out that even with the existing syntax there are oddities, but they can be worked around (quoting the name of the file). Ditto for +.

For ?, it's an alias. Just an alias. Not a command, not an operator. An alias. Plus it is familiar in other languages for simplifying code when you don't want to add all of the checks just to avoid something. The same holds true here with the relaxed scenario. Maybe +? if you really want it, but I think a single symbol is enough.

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

Ruby's * isn't terrible either tbh...

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

As @Jaykul points out, using + formatting would break existing behaviour, which we should probably avoid.

I'm thinking * is probably the most clear and least breaking option at this point. But yeah, we're running short on available symbols here! 😂

from powershell-rfc.

KirkMunro avatar KirkMunro commented on June 16, 2024

@vexx32 @Jaykul It wouldn't break existing behavior at all.

Try this:

function +2boo {'boo boo'}
$Host.UI.Write(+2boo) # generates error

# You can only invoke it in arguments like this:
$Host.UI.Write((+2boo))

So nothing existing would break.

The only potential conflict or confusion is with unary +, which only matters before variables and numeric literals.

So, no breaking changes to avoid here, unless I'm missing something.

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

See, that's what I thought it would do, but @Jaykul decided to be confusing today. 😄

However, in command arguments, it still stands and would conflict with something like -OutVariable +varname

Yes it can be circumvented but it would be breaking some backwards compatibility.

from powershell-rfc.

KirkMunro avatar KirkMunro commented on June 16, 2024

@vexx32 At some point edge cases are determined to be so far off the edge that they don't matter. This is one example of such an edge case.

If you even try to use that in a command, look at what you get:

gps -id $pid -ov +splatme # runs with no error, but doesn't produce what was requested, feels buggy
gv '+splatme' # error, not found
gv splatme # This is the variable that gets created from that edge case
$+possible = $true # error, term not recognized as cmdlet, function, etc.
${+possible} = $true # works, and the only way it seems you'll get a var that starts with +

# Similar:
gps -id $pid -ov @splatmeagain # runs with no error, but that feels like a bug
gv '@splatmeagain' # error, not found
gv splatmeagain # error, not found
$@splatmeagain = $true # error, term not recognized as cmdlet, function, etc.
${@splatmeagain} = $true # works, and the only way it seems you'll get a var that starts with @

All odd, barely functional, to the point where isn't really a valid argument to identify this as a conflict. At best it's worth a very short sentence about a potential breaking change that no one will ever see added to the release notes.

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

@KirkMunro actually, + in a variable-name common parameter is designed to function that way and is intended to mean "append to the content of these variable". That is why looking for a variable named $+var won't work as you're expecting.

The other cases are unrelated, because as you show technically any character is valid in a variable name if you enclose the variable name in braces. Apart from the special treatment of + they simply accept whatever string you give them, because it's a string parameter. Perhaps that should be changed to accept only standard variable name characters, but that's an entirely separate issue.

from powershell-rfc.

KirkMunro avatar KirkMunro commented on June 16, 2024

@vexx32 Thanks, I had forgotten that functionality (never used it). Still, since it's after a named parameter (you can't use it positionally), the parser could handle it, and still support + for splatting.

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

Aye, it could, but such strange features are, I feel, just another reason why it wouldn't be the best idea to use + for this, specifically.

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

It makes less sense to me to use it here since that same sigil is used to declare arrays and hashtables. Seems... Counterintuitive (and messy) to use it to unravel them as well.

I get that we're all used to it's but PowerShell is built on syntactic clarity and verbosity for the most part. Permitting (nay, requiring in some cases) a syntax like @@{name = value } seems just... Wrong.

But c'est la vie. If y'all want that I'm not gonna be the one person to prevent it happening entirely over a silly sigil.

I just think it's decidedly un-PowerShell.

@lzybkr hey, F# uses it, why not us? ;)

Hehe, just kidding. Yeah, I mean, we all agree this is something we'd love to use, so ultimately the syntax needs to work. (I'd still prefer it looked better but function is also necessary here.)

If @@{} is the price we have to pay to make it happen... So be it. :)

from powershell-rfc.

KirkMunro avatar KirkMunro commented on June 16, 2024

I agree with @vexx32: multiple symbols just makes for one ugly syntax. Maybe I'm spoiled by the wonderful syntax improvements that keep being delivered by the C# team, but multiple adjacent symbols certainly doesn't feel like anything close to what I would consider a wonderful syntax improvement.

The only locations where multiple symbols come up are:

  • inline splatting
  • relaxed splatting

Looking again at relaxed splatting, it isn't needed. Just add some extension methods to a hashtable for Take and Leave scenarios, and you can work with a single table in a variable while splatting what you need where you need it. Better to create those extension methods and let them meet that need, IMHO.

That leaves inline splatting.

@@{...}, @@(...), and @$(...) are just ugly syntaxes. The last one feels a little less necessary than the first two (does PowerShell really need to be able to splat the result of a subexpression?). And for the first two, if that's all you've got to offer, I'll just stick with backticks for line continuance and ignore inline splatting in my PowerShell work.

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

I'm on board with all of that. Frankly, the point about naming method parameters is what drew me here in the first place. If those could be implemented that simply I'd be completely on board. (In fact, that method parameter naming's exactly what I've been trying to figure out how to do for the past week; it seems to be a lot more complicated than the suggested syntax makes it look!)

@[] is a syntax I rather like, and is sufficiently different to both @() and @{} to be recognisable yet distinct.

As for extending .Where{} to any IDictionary -- hell yes, that would be awesome. That's probably something that could be implemented in a more generic fashion at another time, though.

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

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

from powershell-rfc.

KirkMunro avatar KirkMunro commented on June 16, 2024

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)

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

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

from powershell-rfc.

Jaykul avatar Jaykul commented on June 16, 2024

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

from powershell-rfc.

felixfbecker avatar felixfbecker commented on June 16, 2024

@KirkMunro that's not splatting, those are named parameters.

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

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

from powershell-rfc.

Jaykul avatar Jaykul commented on June 16, 2024

@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 @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 )

from powershell-rfc.

vexx32 avatar vexx32 commented on June 16, 2024

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

from powershell-rfc.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.