11

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
2
  • I'll update the question with what I'm actually running. It's very similar though. Commented Feb 3, 2017 at 10:45
  • Ah, missed the assignment of the return value. Okay, makes sense now :) Commented Feb 3, 2017 at 10:49

1 Answer 1

14

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
Sign up to request clarification or add additional context in comments.

4 Comments

Okay, wow. That totally makes sense. I never knew ScriptBlock actually had a GetNewClosure method. Most languages aren't that explicit about closures :)
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 :-)
Didn't know that either. Nice one!
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 :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.