Skip to content

Expose the .ForEach() and .Where() methods as regular PowerShell operators #6576

@mklement0

Description

@mklement0

Note:


The hidden and effectively undocumented .Where() and .ForEach methods available on every object exposed by PowerShell were introduced to support DSC in v4 and provide functionality similar to the Where-Object and ForEach-Object cmdlets, but (a) in a more performant way (albeit in an all-in-memory way) and (b) with more features. The most comprehensive documentation that I'm aware of is this blog post by @KirkMunro.

While they could simple be documented (and made more discoverable), I suggest surfacing them as bona fide PowerShell operators -foreach and -where:

  • No native PowerShell features that I am aware of are exposed as methods: instead, functionality is surfaced as commands (cmdlets/functions) and operators.

    • Methods belong to a different realm, to which Get-Help about_Methods is the portal (albeit one that, unfortunately, currently doesn't discuss syntax pitfalls and doesn't even reference Get-Help about_Parsing); it's a powerful realm and definitely worth knowing about, but it is a different realm, distinct from how PowerShell itself surfaces its functionality.
  • The .Where() and .ForEach() methods were introduced as such for the benefit of DSC - of necessity, I presume, given that the DSC DSL doesn't allow use of pipelines (see comments below).

    • Despite their general availability inside PowerShell itself, they're effectively undocumented, and many people may not even be aware of their existence (since tab completion doesn't help (see comments below), and neither Get-Member nor .psobject.Methods find them, you simply have to know of their existence).

    • Even if fully documented (and, as discussed, this is currently even lacking in the DSC context, and overall Kirk's blog post is still the most complete documentation), these methods make awkward tools in general PowerShell use, due to their method syntax and output data types.

Hence my suggestion to surface them as operators with [object[]] output, which would make them discoverable interactively (-<tab>) , and they would be documented alongside the existing operators.

They'd make concise complements to their cmdlet counterparts; -foreach would make the foreach loop unnecessary in many situations and with its use of $_ reduce the existing confusion between the loop and the cmdlet (see MicrosoftDocs/PowerShell-Docs#1514 and a rejected attempt to harmonize the two, #3830):

$var = 1, 2, 3 -foreach { $_ + 1 }  # wishful thinking

# vs.

$var = foreach ($i in 1, 2, 3) { $i + 1 }
# or (slower)
$var = 1, 2, 3 | ForEach-Object { $_ + 1 }

#  ---

$var = 1, 2, 3 -where { $_ -gt 1 } # wishful thinking

# vs.
# (slower)
$var = 1, 2, 3 | Where-Object { $_ -gt 1 }
# or
$var = foreach ($i in 1, 2, 3)  { if ($i -gt 1) { $i } }

Arguably, even DSC could benefit from the cleaner operator syntax; contrast the following:

# Note that this example, taken from https://docs.microsoft.com/en-us/powershell/dsc/separatingenvdata
# doesn't even use `()` around the script-block argument, so the use of Where() 
# isn't even readily recognizable as a *method* call.
Node ($AllNodes.Where{$_.Role -eq "WebServer"}).NodeName { ...

# vs.

Node ($AllNodes -where {$_.Role -eq "WebServer"}).NodeName { ...  # wishful thinking

Environment data

Written as of:

PowerShell Core v6.0.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Enhancementthe issue is more of a feature request than a bugResolution-ExternalThe issue is caused by external component(s).

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions