Who has seen this potential powershell object property treated as method call booby trap?

As - perhaps unfortunate - syntactic sugar, PowerShell allows you to shorten:

$object.Method({ ... })

to:

$object.Method{ ... }

Note:

  • In both cases there mustn't be a space after the method name (whereas C# allows "foo".Substring (1), for instance).

  • Method in the above example merely has to be syntactically valid as a method name in order for both expressions to be treated as method calls - a method call is attempted even if no such method exists or if the name happens to refer to a property instead.

In other words:

  • Methods that accept exactly one (non-optional) argument of type script block ([scriptblock]; { ... }) allow invocation without parentheses ((...)).

Arguably, such a narrow use case wouldn't have called for syntactic sugar:

  • Limiting support to script blocks limits the syntactic sugar to PowerShell-provided/-targeted types and their methods, given that script blocks are a PowerShell-specific feature.

  • The requirement not to separate the name and the opening { with a space is at odds with how script blocks are customarily passed to cmdlets (e.g. 1, 2, 3 | ForEach-Object { $_ + 1 } vs. (1, 2, 3).ForEach{ $_ + 1 } - see below)

  • Having to switch back to (...) as soon as two or more arguments must be passed is awkward.

Presumably, this was introduced to cut down on the "syntactic noise" of one common scenario: the use of the PSv4+ .ForEach() and .Where() array methods, introduced for the DSC (Desired State Configuration) feature, which are typically invoked with only a script block; e.g.:

  • (1, 2, 3).ForEach({ $_ + 1 }) can be simplified to (1, 2, 3).ForEach{ $_ + 1 }

As for documentation:

  • The behavior is described only in the context of the aforementioned .ForEach() and .Where() methods in the context of the conceptual about_Arrays help topic:

The syntax requires the usage of a script block. Parentheses are optional if the scriptblock is the only parameter. Also, there must not be a space between the method and the opening parenthesis or brace.

  • Given that it applies to any method with the appropriate signature (irrespective of the .NET type and irrespective of whether it is an instance or a static method), it should arguably (also) be documented in the conceptual about_Methods help topic, which isn't the case as of this writing.

Even with coverage in about_Methods, however, the challenge is to even infer that a method call is being attempted when you don't expect that - at least in the switch case the error message doesn't help.


Design musings:

Note that the .ForEach() and .Where() methods are generally a somewhat awkward fit, given that most native PowerShell features do not rely on methods, and that even regular method-invocation syntax can cause confusion with the shell-like syntax used to invoke all other commands (cmdlets, functions, scripts, external programs) - e.g., $foo.Get('foo', bar') vs. Get-Foo foo bar.

This rejected RFC on GitHub proposed introducing -foreach and -where operators, which would have allowed the following, more PowerShell-idiomatic syntax:

  • 1, 2, 3 -foreach { $_ + 1 }
  • 1, 2, 3, 2 -where { $_ -eq 2 }, 'First'