Add lexically scoped variables with variable capture - by Derp McDerp

Status : 

 


1
0
Sign in
to vote
ID 833124 Comments
Status Active Workarounds
Type Suggestion Repros 0
Opened 3/12/2014 10:32:32 PM
Access Restriction Public

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 {
}
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