Home Dashboard Directory Help
Search

Add lexically scoped variables with variable capture by Derp McDerp


Status: 

Active


1
0
Sign in
to vote
Type: Suggestion
ID: 833124
Opened: 3/12/2014 10:32:32 PM
Access Restriction: Public
0
Workaround(s)
view

Description

The reserved keyword "var" should be used to create lexically scoped variables and these variables can be captured by scriptblock literals. Also, scoped $:foo should reference dynamically scoped variables only and bare $foo can reference lexical or dynamically scoped variables.

The paper "Closures in Lua" describes a very simple algorithm for capturing lexical variables which can be trivally be implemented in PowerShell.

& {
    var $foo = 3 # create a new lexically scoped $foo

    var $:foo = 2 # error, can't mix scope with lexical variables
    var $variable:foo = 2 # error, can't mix scope with lexical variables
    var $private:foo = 2 # error, can't mix scope with lexical variables
    var $local:foo = 2 # error, can't mix scope with lexical variables
    var $script:foo = 2 # error, can't mix scope with lexical variables
    var $global:foo = 2 # error, can't mix scope with lexical variables
    var $use:foo = 2 # error, can't mix scope with lexical variables
    var $whatever:foo = 2 # error, can't mix scope with lexical variables

    $foo
    # bare 'foo' first looks up lexically scoped 'foo', if there is no
    # 'foo' in the lexical symbol table, it searches for dynamically
    # scoped 'foo'. The lexical scope lookup is all done at compile time.

    $:foo
    # scoped 'foo' looks up dynamically scoped foo only ignoring the
    # lexical scope

    if (blah) {
        $foo = 4 # modifies outer lexically scoped $foo
    } else {
        var $foo = 4
        # creates a new $foo only visible in the inner else scope

        var $foo = 5
        # error, 2 lexical $foos declared in the same block scope
    }

    Get-Variable foo
    # lexical variables are completely invisible to Get-Variable

    "asdf ${foo} asdf"
    # accesses lexical 'foo'

    "asdf ${:foo} asdf ${variable:foo}"
    # accesses dynamic 'foo'

# variable capture examples:

    $a = { $foo + $args[0] }
    # scriptblock captures the lexical $foo in scope here

    $b = [scriptblock]::Create('$foo + $args[0]')
    # the $foo here only accesses the dynamically scoped $foo
    # there is no way to dynamically access lexical variables
    # with "eval" like code

    var $i = 3
    $a = @()
    foreach (var $i in 0..10) {
        $a += { $i }
    }
    # $i is 3 here
    foreach (var $a in $:a) {
        Write-Host (& $a)
    }
    # prints 1..10
<#

e.g.

    foreach (var $i in 0..10) {
        $a += { $i }
    }

acts like:

    foreach ($null in 0..10) {
        var $i = $foreach.Current
        $a += { $i }
    }

#>
    var $b = 3
    if (var $b = 2) {
    }
    # $b is 3 here
}


If the var keyword precedes the param keyword:

[CmdletBinding()]
var param( $Foo , $:Bar )

1. All the default variables (i.e. System.Management.Automation.AutomaticVariable) like $args, $this, $PSCmdlet become lexical variables instead of dynamic.
2. All unscoped param()s become lexical variables, e.g. in the above example, $Foo would become lexical and $Bar would become dynamic


begin {
    var $a = 3
    # $a is visible in begin, process, end
    # $a's lifetime ends in end
}
process {
    var $b
    # $b is visible in process
    # $b's lifetime ends in process
}
end {
}
Details
Sign in to post a comment.
Posted by Derp McDerp on 3/12/2014 at 10:34 PM
Lexically scoped variables will take care of most uses of the following 2 feature requests:

https://connect.microsoft.com/PowerShell/feedback/details/805570/support-invocation-of-script-blocks-in-the-parent-scope-of-a-function
https://connect.microsoft.com/PowerShell/feedback/details/525318/v3-suggestion-allow-execution-of-scriptblocks-at-arbitrary-scopes
Sign in to post a workaround.