Reputation: 1813
The following PowerShell code displays unexpected scoping behavior for functions called from closures. Can you explain whether this is "by design", or is a defect?
function runblock($block) {
$x = 4
& $block
}
function printx() {
" in printx: x=" + $x
}
"PSVersion $($PSVersionTable.PSVersion)"
$x = 1
$b = {"In block x=" + $x ; $x = 3 ; printx}
$x = 2
runblock $b
$x = 1
$b = {"In closure x=" + $x ; $x = 3 ; printx}.GetNewClosure()
$x = 2
runblock $b
Output from the above is
PSVersion 3.0
In block x=4
in printx: x=3
In closure x=1
in printx: x=4
Most of the output makes sense to me:
The script block outputs In block x=4
since its parent scope is the runblock
function. The printx
function outputs x=3
since its parent scope is the script block scope.
The closure outputs In closure x=1
since the value of $x
was captured by GetNewClosure
call. All as expected.
BUT:
The call to printx
from the closure outputs in printx: x=4
. So the scope that printx
executes within is unaffected by the scope of the closure where $x = 3
.
It seems strange to me that a function called from a normal script block does see the variables in the script block scope, but a function called from a closure does not see the variables in the closure.
Upvotes: 3
Views: 1841
Reputation: 22132
Consider following code:
function Run {
param($ScriptBlock)
$a = 3
# Picture 4
& $ScriptBlock
}
function Print {
# Picture 6
"Print `$a=$a"
"Print `$b=$b"
}
$a = 1
$b = 1
# Picture 1
$SB = {
# Picture 5
"Closure `$a=$a"
"Closure `$b=$b"
Print
}.GetNewClosure()
# Picture 2
$a = 2
$b = 2
# Picture 3
Run $SB
It prints:
Closure $a=1
Closure $b=1
Print $a=3
Print $b=2
Picture 1: you start from global scope, where you define some variables.
Picture 2: GetNewClosure()
create new module and copy variables to it. (red arrow show parent scope relationship)
Picture 3: you change value of variables. Module scope not affected.
Picture 4: function Run
called. It create local variable $a
. (blue arrow show call direction)
Picture 5: $SB
script block called. Script block bound to module session state, so you transit to it.
Picture 6: function Print
called. Function bound to global session state, so you return to it.
Upvotes: 13