Skip to content

Commit 191e91a

Browse files
mklement0joeyaiello
authored andcommitted
Reject RFC0034 for .ForEach() and .Where() methods as regular PowerShell operators (#126)
* Create RFCNNNN-Expose-ForEach-Where-Methods-as-Operators.md Follow-up from discussion at PowerShell/PowerShell#6576 * Update RFCNNNN-Expose-ForEach-Where-Methods-as-Operators.md * Update RFCNNNN-Expose-ForEach-Where-Methods-as-Operators.md * Update RFCNNNN-Expose-ForEach-Where-Methods-as-Operators.md * Update RFCNNNN-Expose-ForEach-Where-Methods-as-Operators.md * Update RFCNNNN-Expose-ForEach-Where-Methods-as-Operators.md * Update RFCNNNN-Expose-ForEach-Where-Methods-as-Operators.md * operator precedence and error handling addressed; doc note amended * Reject RFC0034 for -where and -foreach operators
1 parent 0f56339 commit 191e91a

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
---
2+
RFC: RFC0034
3+
Author: Michael Klement
4+
Status: Rejected
5+
Version: 0.1
6+
Area: Operators
7+
Comments Due: 7/8/2018
8+
Plan to implement: No
9+
---
10+
11+
# Expose the .ForEach() and .Where() collection-transforming/filtering methods as regular PowerShell operators
12+
13+
[The `.ForEach()` and `.Where()` methods](http://www.powershellmagazine.com/2014/10/22/foreach-and-where-magic-methods/) introduced in PSv4 are better-performing and feature-richer expression-mode alternatives to the `ForEach-Object` and `Where-Object` cmdlets; they complement the memory-throttling but slow pipeline processing by the cmdlets with fast, all-in-memory, collected-up-front collection processing.
14+
15+
Semantically, these methods act _like_ operators (and are even referred to a such in source code and documentation fragment), but are syntactically implemented as _methods_.
16+
17+
Since PowerShell's _own_ functionality (as opposed to .NET functionality made _accessible_ by it) is surfaced as commands (cmdlets/function/scripts) and _bona fide operators_ such as `-match`, `.ForEach()` and `.Where()` should (also) be surfaced as array-valued binary operators `-foreach` and `-where`.
18+
19+
## Motivation
20+
21+
As a regular PowerShell user
22+
I can use operators `-foreach` and `-where`
23+
in order to transform/filter in-memory collections efficiently, using familiar operator syntax and `$_`-based script blocks, as conceptually clean complements to the `ForEach-Object` and `Where-Object` cmdlets.
24+
25+
Examples:
26+
27+
```powershell
28+
$var = 1, 2, 3 -foreach { $_ + 1 } # wishful thinking
29+
30+
# vs.
31+
$var = foreach ($i in 1, 2, 3) { $i + 1 }
32+
# or (slower)
33+
$var = 1, 2, 3 | ForEach-Object { $_ + 1 }
34+
# ---
35+
36+
$var = 1, 2, 3 -where { $_ -gt 1 } # wishful thinking
37+
38+
# vs.
39+
# (slower)
40+
$var = 1, 2, 3 | Where-Object { $_ -gt 1 }
41+
# or (conceptually *indirect*, given that there's no filtering loop):
42+
$var = foreach ($i in 1, 2, 3) { if ($i -gt 1) { $i } }
43+
```
44+
45+
Advanced example (no `Where-Object` counterpart): split a collection in two:
46+
47+
```powershell
48+
$odds, $evens = 1, 2, 3, 4 -where { $_ % 2 }, 'split'
49+
50+
# equivalent to:
51+
$odds, $evens = (1, 2, 3, 4).Where({ $_ % 2 }, 'split')
52+
```
53+
54+
55+
## Specification
56+
57+
* `.ForEach()` and `.Where()` will be exposed as _binary operators_ `-foreach` and `-where`, respectively.
58+
59+
* Unlike the methods (which return `[System.Collections.ObjectModel.Collection[psobject]]` instances), the operators will return `[object[]]` arrays for consistency with existing array-aware operators such as `-eq` and `-match`.
60+
61+
* The _optional_ arguments supported by the `.ForEach()` and `.Where()` methods will be surfaced as optional RHS array elements, analogous to the `-split` operator's optional arguments, for instance - see below.
62+
63+
* The new operators will have the same precedence as the group of equal-precedence operators that operator `-like` falls into - see [`Get-Help about_Operator_Precedence`](https://github.com/PowerShell/PowerShell-Docs/blob/staging/reference/6/Microsoft.PowerShell.Core/About/about_Operator_Precedence.md)
64+
65+
* In terms of handling errors occurring in the RHS script block as well as `return`, `continue` and `break` semantics there, the new operators will exhibit the same behavior as the `.ForEach()` and `.Where()` methods.
66+
67+
### Syntax forms (meta syntax borrowed from [`about_Split`](https://github.com/PowerShell/PowerShell-Docs/blob/staging/reference/6/Microsoft.PowerShell.Core/About/about_Split.md]))
68+
69+
#### `-foreach`
70+
71+
For script-block-based transformation, optionally with arguments passed to each script block invocation.
72+
73+
```none
74+
<collection> -foreach <ScriptBlock>[, <Arguments[]>]
75+
```
76+
77+
For type conversion.
78+
Note:
79+
80+
* Arguably, this form is not needed, because a simple array-valued cast will do:
81+
`[string[]] (1, 2, 3)` rather than `1, 2, 3 -foreach [string]`
82+
83+
* However, for symmetry with .ForEach() it should probably be implemented.
84+
85+
```none
86+
<collection> -foreach <Type>
87+
```
88+
89+
For property access / method calls:
90+
Note:
91+
92+
* For mere property extraction, there's a simpler alternative: member enumeration:
93+
`((get-date), (get-date)).Ticks` vs. `(get-date), (get-date) -foreach 'Ticks'`
94+
95+
* However, there's actually a distinct advantage to using `-foreach`: bypassing the _ambiguity_ of member enumeration:
96+
`('ab', 'cde') -foreach 'Length'` will unambigiously access the _elements'_ `.Length` property,
97+
whereas `('ab', 'cde').Length` returns the _array's_ length (element count).
98+
99+
```none
100+
<collection> -foreach "propertyName"[, <value>]
101+
<collection> -foreach "methodName"[, <Arguments[]>]
102+
```
103+
104+
#### `-where`
105+
106+
Filtering a collection based on the Boolean outcome of a script block, optionally in one of serveral modes and with a limit on how many objects to return:
107+
108+
```none
109+
<collection> -where <ScriptBlock>[, <mode>[, <numberToReturn>]]
110+
```
111+
112+
* `<mode>` is technically a [`[System.Management.AutomationWhereOperatorSelectionMode]`](https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.whereoperatorselectionmode?view=powershellsdk-1.1.0) enumeration value, but may be specified as a string (e.g., `'Split'`)
113+
* The semantics of `[int]` `<numberToReturn>` depend on `<mode>`; `0`, the default, requests that _all_ matching elements be returned.
114+
* Given https://github.com/PowerShell/PowerShell/issues/4765, consider interpreting negative values - which currently generate an execption - as requests to return elements from the _end_ of the results collection - this should obviously apply both to the method and operator forms.
115+
* As with `-split`, arguments are strictly positional; notably, specifying `<numberToReturn>` requires that `<mode>` value also be specified, even if as `'Default`.
116+
117+
[@KirkMunro's blog post](http://www.powershellmagazine.com/2014/10/22/foreach-and-where-magic-methods/) contains the details.
118+
119+
It's probably worth creating a new conceptual help topic titled `about_Collection_Operators` to describe these new operators, with links to existing help topics for those existing operators that _also_ support collection-valued LHS input, such as `-eq`, `-like`, and `-replace`, among others.
120+
121+
## Alternate Proposals and Considerations
122+
123+
The alternative is to make do with the existing _method_ implementation, restricting their use to a more developer-savvy crowd comfortable with method syntax.
124+
125+
Note that even the existing methods [lack proper documentation](https://github.com/PowerShell/PowerShell-Docs/issues/2307) as of this writing.

0 commit comments

Comments
 (0)