PowerShell's parsing modes: argument (command) mode vs. expression mode
PowerShell has two fundamental parsing modes:
argument mode, which works like traditional shells
- In argument mode, the first token is interpreted as a command name (such as cmdlet name, function name, or filename of a script or external executable), followed by a whitespace-separated list of arguments.
expression mode, which works like traditional programming languages.
Running Get-help about_Parsing
provides an introduction to these modes; in short, it is the first token that determines which mode is applied.
Also note that a given statement may be composed of parts that are parsed in either mode.
boolean -eq $false
is parsed in argument mode, because its first token looks like a command name (an identifier that could be a program name, cmdlet name, function name, or alias).
Therefore, -eq
and $false
are interpreted as arguments (parameter values) to pass to function boolean
.
Since your boolean
functions are defined in a way that doesn't enforce passing values only to declared parameters, the arguments are effectively ignored, and the result of the statement is whatever the functions output ($false
or $true
).
As demonstrated in Mike Shepard's answer, you can make a function enforce use of only declared parameters (including none) with a param()
block decorated with the [CmdletBinding()]
attribute (which makes the function an advanced function), which would at least result in an error if you inadvertently passed arguments to the parameter-less boolean
function.
You can work around this by setting the function call to a variable
$bool = boolean # execute function and capture result in variable
$bool -eq $false # use variable in the comparison
The reason this works is that the -eq
statement starts with $
- a variable reference in this case - which causes PowerShell to parse in expression mode, where -eq
is recognized as an operator and $false
as its RHS.
However, there is no need for this intermediate step:
To force a piece of code to be interpreted as an expression, enclose it in (...)
, the grouping operator:
(boolean) -eq $false # Calls function 'boolean' and uses result as LHS of -eq
(...)
forces a new parsing context (which in itself is parsed in either argument or expression mode, again depending on the 1st token) and treats the result as an expression. which then allows its use as part of a larger expression, such as as an operand of the -eq
operator, or as a command argument.
PowerShell is seeing -eq as the name of a parameter being passed to the "boolean" function.
To see this, you can put the function call in parens:
function boolean {
return $false
}
(boolean) -eq $false
function boolean {
return $true
}
(boolean) -eq $true
Or, you can make it an advanced function so you get an error with the missing parameter (-eq):
function boolean {
[CmdletBinding()]
Param()
return $false
}
boolean -eq $false
function boolean {
[CmdletBinding()]
Param()
return $true
}
boolean -eq $true