Powershell script blocks are not closures

I've been experimenting some more with the script blocks, and I've found that my description of them as sort-of-closures in my other post is wrong. Here is an example that demonstrates it:

PS > function g { param($block) $a=234; &$block; }
PS > function f { $a=123; g { "The value of `$a is $a" } }
PS > f
The value of $a is 234

They simply use the variable values from the nearest enclosing block in the same module.

 But there is a way to get a sort-of-closure too:

PS > function f { $a=123; g { "The value of `$a is $a" }.GetNewClosure() }
PS > f
The value of $a is 123

The sort-of part is because the value of $a in f() cannot be changed from inside the script block.

My next idea was to use the references but that didn't quite work out either:

PS > function f { $a=123; g { $aa=[ref]$a; "The value of `$a is $($aa.value)"; $aa.value = 10; "The new value of `$a is $($aa.value)" }.GetNewClosure(); $a }
PS > f
The value of $a is 123
The new value of $a is 10

The problem is GetNewClosure() doesn't create a reference to the variables in the current scope but just copies their values to the new variables. Thus [ref] gets a reference to the copy, and this copy is not visible inside the function f().

Without GetNewClosure, the script block ends up changing the value of the variable in the nearest scope, which happens to be in g():

PS > function g { param($block) $a=234; &$block; "in g `$a is $a" }
PS > function f { $a=123; g { $aa=[ref]$a; "The value of `$a is $($aa.value)"; $aa.value = 10; "The new value of `$a is $($aa.value)" }; $a }
PS > f
The value of $a is 234
The new value of $a is 10
in g $a is 10

The way to make it behave like a proper closure, being able to change the variables in the original scope, is to get the reference directly in f() and then fixate it with GetNewClosure():

PS C:\Users\sbabkin> function f { $a=123; $aa=[ref]$a; g {"The value of `$a is $($aa.value)"; $aa.value = 10; "The new value of `$a is $($aa.value)" }.GetNewClosure(); $a }
PS C:\Users\sbabkin> f
The value of $a is 123
The new value of $a is 10
in g $a is 234

It finally works but oh so painfully.

Comments (4)

  1. Bruce Payette says:

    Hi Sergey – yes – closures are pretty weird in PowerShell, This is because the language is (kind of) dynamically scoped. All variables from the call (dynamic) scope are visible when a function is executed. Proper closures really require lexical scoping. However, the GetNewClosure() method allows you to have some of the benefits of proper closures in a dynamically scoped language. Basically what it does is create a new anonymous module, copies the variables from the nearest enclosing dynamic scope into that module and then binds the scriptblock to that module. When the scriptblock is executed it sees what was copied into that module. So you can, in fact, change these value by using the $script:var scope qualifier. From your example, this changes the value of abc:  

    function f { $a=123; { "The value of `$a is $a"; $script:a= "Hi there" ; "The value of `$a is $a";  }.GetNewClosure() }

    I go into a lot more detail on this stuff in my book.



    1. Adam Daughterson says:

      I’m really interested in accessing class methods from within a ScriptBlock being remotely executed, and having no luck at all finding anyone with experience in this.
      I’ve had the though that creating closures which reference the class methods available in the enclosing scope, but this seems REALLY kludgey.
      Any suggestions are appreciated immensely.



      1. The remote execution is a pretty hard boundary: you can’t access anything across it. Nothing defined locally is visible remotely. I’ve had a similar problem when I needed to call in the remote block the functions I’ve defined locally, and I’ve ended up with writing a function that exports the local functions to the remote side. It basically takes the name of the function to export as an argument, uses it to find the body of the function, then sends the name and body to the remote side and creates the matching function there. The same goes for the variables, if you want to read a variable remotely, you have create a remote copy of it.

  2. Is there some reason why the variables are copied by value and not simply referenced? I think the trick with [ref] shows that if the original variables were referenced, they could be modified from the script blocks.

Skip to main content