Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explain scoping for functions called from PowerShell closures

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.

like image 200
John Rees Avatar asked Jul 21 '26 05:07

John Rees


1 Answers

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 1
Picture 2: GetNewClosure() create new module and copy variables to it. (red arrow show parent scope relationship)
Picture 2
Picture 3: you change value of variables. Module scope not affected.
Picture 3
Picture 4: function Run called. It create local variable $a. (blue arrow show call direction)
Picture 4
Picture 5: $SB script block called. Script block bound to module session state, so you transit to it.
Picture 5
Picture 6: function Print called. Function bound to global session state, so you return to it.
Picture 6

like image 99
user4003407 Avatar answered Jul 25 '26 00:07

user4003407



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!