Skip to content

Commit 837e1a0

Browse files
committed
RFC-Draft
1 parent e48c03c commit 837e1a0

File tree

1 file changed

+251
-0
lines changed

1 file changed

+251
-0
lines changed
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
---
2+
RFC: 0002
3+
Author: Jason Shirk
4+
Status: Draft
5+
Area: Splatting
6+
Comments Due: 3/31/2016
7+
---
8+
9+
# Generalized Splatting
10+
11+
Splatting was introduced in PowerShell v2
12+
as a way of passing named arguments to a command
13+
using an expression (e.g. `@PSBoundParameters`)
14+
instead of explicit parameter syntax (e.g. `-Param1 $value`).
15+
The expression syntax introduced was limited
16+
to variable references and is more restrictive than necessary.
17+
18+
Proxy functions are much easier to write using splatting.
19+
A proxy function might add or remove parameters to another command,
20+
but might not care about all of the parameters to the proxied command.
21+
The proxy still needs to pass those parameters through to the proxy,
22+
and it does this with splatting.
23+
24+
Scripters have found splatting useful beyond proxy functions,
25+
but the current syntax limits broader use of splatting.
26+
27+
## Motivation
28+
29+
One common use of splatting is to provide an alternate syntax for calling a command with many parameters.
30+
For example, consider a command like the following
31+
32+
```PowerShell
33+
Update-TypeData -TypeName MyCustomType `
34+
-MemberName MyCustomProperty `
35+
-MemberType NoteProperty `
36+
-Value 42
37+
```
38+
39+
In the above example, note the use of backticks at the end of every line.
40+
Those backticks are difficult to read and easy to forget.
41+
Splatting can help some here:
42+
43+
```PowerShell
44+
$addTypeParams = @{
45+
TypeName = 'MyCustomType'
46+
MemberName = 'MyCustomProperty'
47+
MemberType = 'NoteProperty'
48+
Value = 42
49+
}
50+
Update-TypeData @addTypeParams
51+
```
52+
53+
This works, but feels a bit messy because of the need for a variable,
54+
when the hashtable could start on the same line as the command name.
55+
56+
Another common use of splatting is to remove a specific parameter when splatting:
57+
58+
```PowerShell
59+
$PSBoundParameters.Remove('SomeExtraParam')
60+
Command @PSBoundParameters
61+
```
62+
63+
This proposal suggesst a syntax that improves this scenario as well.
64+
65+
## Specification
66+
67+
This RFC proposes a more generalized syntax for splatting to support:
68+
69+
- Splatting general expressions (as opposed to simple variables)
70+
- Splatting in method invocations
71+
- Splatting in switch cases
72+
73+
Today, the syntax for splatting is a sigil used on a variable name.
74+
This proposal expands the use of the sigil to be allowed on an expression.
75+
For example:
76+
77+
```PowerShell
78+
# Existing usage:
79+
command @PSBoundParameters
80+
81+
# Equivalent - but splatting an expression (the value of a variable)
82+
command @$PSBoundParameters
83+
84+
# Splat another expression - a hashtable
85+
Update-TypeData @@{
86+
TypeName = 'MyCustomType'
87+
MemberName = 'MyCustomProperty'
88+
MemberType = 'NoteProperty'
89+
Value = 42
90+
}
91+
```
92+
93+
Note the two '@' characters in the hashtable example above.
94+
If the second '@' was omitted, the example would pass a hashtable (no splatting)
95+
in all versions of PowerShell, even V1, which means splatting without the second '@'
96+
would be a breaking change.
97+
98+
We can use more general expressions as well:
99+
100+
```PowerShell
101+
# Call a command that returns a hashtable or array to splat
102+
Get-ChildItem @$(Get-ChildItemArgs)
103+
104+
# a method that returns a hashtable or array to splat
105+
Invoke-Something @$($obj.GetInvokerArgs())
106+
```
107+
108+
### Relaxed splatting
109+
110+
In some cases, it is desirable to splat only the arguments that are available,
111+
and ignore the others.
112+
We will call this relaxed splatting.
113+
For example:
114+
115+
```PowerShell
116+
$myArgs = @{ Path = $pwd; ExtraStuff = 1234 }
117+
Get-ChildItem @$myArgs
118+
```
119+
120+
The above example would fail with a "parameter not found" because of the 'ExtraStuff' key.
121+
Here is a possible syntax to allow the above without resulting in an error:
122+
123+
```PowerShell
124+
$myArgs = @{ Path = $pwd; ExtraStuff = 1234 }
125+
Get-ChildItem @?$myArgs
126+
```
127+
128+
We can think of '@?' as the 'relaxed splatting' operator.
129+
If '@' is the splatting operator,
130+
adding the '?' is suggestive of being more permissive,
131+
much like the C# '?.' member access operator.
132+
133+
### Splatting in method invocations
134+
135+
Today, named arguments are only supported when calling commands,
136+
named arguments do not work when calling methods.
137+
PowerShell could adopt a C# like syntax to name arguments,
138+
but splatting provides a similar capability with some additional flexibility.
139+
The obvious syntax would mirror command invocation:
140+
141+
```PowerShell
142+
# Splat a hashtable defined outside the method call
143+
$subStringArgs = @{startIndex = 2}
144+
$str.SubString(@$subStringArgs)
145+
146+
# Splat a hashtable defined inline in the method call
147+
$str.SubString(@@{startIndex = 2; length=2})
148+
```
149+
150+
Mixing splatting and positional arguments is supported.
151+
152+
```PowerShell
153+
$str.SubString(2, @@{length=2})
154+
```
155+
156+
A runtime check (or parse time if possible) is necessary to make sure an argument is not specified positionally
157+
and via splatting in the same invocation:
158+
159+
```PowerShell
160+
# Must be an error, parse time or runtime, because startIndex
161+
# is specified positionally and via splatting.
162+
$subStringArgs = @{startIndex = 2}
163+
$str.SubString(2, @$subStringArgs)
164+
```
165+
166+
Multiple splatted arguments are not allowed:
167+
168+
```PowerShell
169+
# Error, multiple splatted arguments
170+
$str.SubString(@@{startIndex = 2}, @@{length=2})
171+
```
172+
173+
The splatted argument must be last.
174+
175+
```PowerShell
176+
# Error, splatted argument is not last
177+
$str.SubString(@@{length=2}, 2)
178+
```
179+
180+
### Splatting in switch cases
181+
182+
It can be awkward to match multiple conditions with a single switch statement.
183+
For example:
184+
185+
```PowerShell
186+
switch ($color) {
187+
{ $_ -eq 'Red' -or $_ -eq 'Blue' -or $_ -eq 'Green' } { $true }
188+
default { $false }
189+
}
190+
```
191+
192+
With splatting, this can be simplified:
193+
194+
```PowerShell
195+
switch ($color) {
196+
@@('Red','Blue','Green') { $true }
197+
default { $false }
198+
}
199+
```
200+
201+
### Modifying hashtables for splatting
202+
203+
Sometimes it is useful to provide a 'slice' of a hashtable,
204+
e.g. you want to remove or include specific keys.
205+
The Add/Remove methods on a hashtable work, but can be verbose.
206+
This proposal suggests overloading the '+' and '-' operators to provide a hashtable 'slice':
207+
208+
```PowerShell
209+
Get-ChildItem @$($PSBoundParameters - 'Force') # Splat all parameters but 'Force'
210+
Get-ChildItem @$($PSBoundParameters - 'Force','WhatIf') # Splat all parameters but 'Force' and 'WhatIf'
211+
Get-ChildItem @$($PSBOundParameters + 'LiteralPath','Path') # Only splat 'LiteralPath' and 'Path'
212+
```
213+
214+
Today, PowerShell supports "adding" two hashtables with the '+' operator,
215+
the result is a new hashtable with all of the key value pairs from both hashtables.
216+
It is an error for a key to be specified in both operands.
217+
218+
This proposal adds semantics for adding and subtracting other values.
219+
If the value is enumerable, the effect is to treat each value in the collection as a key,
220+
otherwise the value is treated as a single key.
221+
The result is always a new hashtable,
222+
the left hashtable operand is unmodified.
223+
224+
When using '+', the result will only include keys found in the right hand side.
225+
When using '-', the result will exclude all keys from the right hand side.
226+
227+
In either case,
228+
it is not an error to specify a key in the right hand side operand that is not present in the left hand side.
229+
230+
## Alternate Proposals and Considerations
231+
232+
### Slicing operators
233+
234+
The suggested use of '+' and '-' is perhaps surprising
235+
even though they correspond to Add and Remove, respectively.
236+
The actual operation is also similar to a union or intersection,
237+
so other operators should be considered, perhaps bitwise operators
238+
like '-bnot' and '-bor', or maybe new general purpose set operators.
239+
240+
### Postfix operator
241+
242+
The use of a sigil is not always well received.
243+
This proposal nearly considers '@' a prefix unary operator,
244+
but it doesn't quite specify it as such.
245+
246+
A postfix operator is another possiblity and would look less like a sigil.
247+
This idea was rejected because, when reading a command invocation from left to right,
248+
it's important to understand how a hash literal is to be used.
249+
The sigil makes it clear a hash literal is really specifying command arguments.
250+
Furthermore, the sigil simplifies the analysis required for good parameter completion,
251+
and does not require a complete expression to begin providing parameter name completion.

0 commit comments

Comments
 (0)