PowerShell ScriptBlock variable scope
Asked Answered
T

1

10

I'm having trouble understanding scopes within ScriptBlocks. I was counting on some kind of closure-like system, but I can't seem to get it working.

I have a ScriptBlock that takes a param and returns another ScriptBlock:

$sb1 = {
    Param($Message1);
    Write-Host $Message1;
    {
        Param($Message2);
        Write-Host ($Message1 + " " + $Message2);
    }
}

To get the inner ScriptBlock I can invoke $sb1 with $sb2 = & $sb1 -Message1 "Message1". This echoes Message1 so we know the param is bound.

Now I can invoke $sb2 with & $sb2 -Message2 "Message2". I would have expected Message1 Message2, but it just writes Message2 instead.

Is there any way to access the $Message1 variable? I can't use a local or script variable, because there will multiple instances of the inner scriptblock with different $Message1s.

This is the actual output from the actual shell:

PS C:\> $h1 = { Param($Message1); Write-Host $Message1; { Param($Message2); Write-Host ($Message1 + " " + $Message2); } }
PS C:\> $h2 = & $h1 -Message1 "Message1"
Message1
PS C:\> $h2
 Param($Message2); Write-Host ($Message1 + " " + $Message2);
PS C:\> & $h2 -Message2 "Message2"
 Message2
Turbid answered 3/2, 2017 at 10:34 Comment(2)
I'll update the question with what I'm actually running. It's very similar though.Turbid
Ah, missed the assignment of the return value. Okay, makes sense now :)Debauchery
D
13

You need to explicitly create a closure:

$sb1 = {
    Param($Message1);
    Write-Host $Message1;
    {
        Param($Message2);
        Write-Host ($Message1 + " " + $Message2);
    }.GetNewClosure()
}

It then works for me:

PS> $2 = & $sb1 One
One
PS> & $2 Two
One Two
Debauchery answered 3/2, 2017 at 10:50 Comment(4)
Okay, wow. That totally makes sense. I never knew ScriptBlock actually had a GetNewClosure method. Most languages aren't that explicit about closures :)Turbid
Having the choice can be nice sometimes. I have written scripts where I used scriptblocks just to access variables I knew were there and where having a closure would have been annoying. But yeah, usually it's either automatic or not supported at all :-)Debauchery
Didn't know that either. Nice one!Lepore
And I have to admit, that's the first instance of currying I've seen in PowerShell. Never even occurred to me to do that :-)Debauchery

© 2022 - 2024 — McMap. All rights reserved.