Scope refers to the visibility of various contexts when binding names of variables to values, or when resolving command names.
A name is considered lexically-scoped (statically-scoped) when the object it refers to corresponds to the static layout of the program text, and dynamically-scoped when the object it refers to depends on the program state at the moment the variable is resolved at runtime. The scoping rules of Tcl are primarily dynamic, although name resolution rules involving namespaces have a more lexical flavour. namespaces are reasonably static in the sense that each namespace has an explicit location in the namespace hierarchy, a structure which is used to organize variables and commands, and which has a high correlation to the textual layout of namespaces in the program source code. All Commands and non-local variables are associated with some namespace, and are considered to be mainly lexically-scoped. Local variables are truly lexical since their only context is the immediate text of the command in which they are defined. variable and global can make namespace variables visible in a command. Highly-dynamic scoping is achieved via upvar and uplevel, commands which can be used to do great things — terrible, yes, but great.
The features of Tcl described above allow Tcl programs to be written such that they can be read as if they were for the most part lexically-scoped. Tcl's hybrid approach strikes a great balance in maximizing the advantages of both lexical and dynamic scoping while minimizing the disadvantages of each.
The following namespaces can be in scope when resolving some name:
In addition to general the namespaces provided by Tcl, various commands maintain resources that are accessed via a value that serves as a handle for the resource:
RS:
CMcC believes this isn't true. Firstly, closures can work in either lexically or dynamically scoped languages.
Secondly, Scoping is about how one resolves (or binds) free (or unbound) names. Dynamic binding can only occur at runtime, Static binding can sometimes occur at definition time (which is its supposed advantage).
In dynamically-scoped languages (in, e.g. real Lisp :) the call frame is searched for name->value mappings matching some unbound name - like upvar. In lexically scoped languages (in, e.g. Scheme) the definition environment for the currently executing code is searched for those mappings - like Tcl's variable
NEM One advantage of static scoping is that the environment a function executes in is related to its position in the source code of the program (hence lexical), whereas in dynamic scope the environment is determined by the dynamic flow of control. The advantage is that it should be easier to track down what variable is being referenced. Tcl uses a limited form of static scope for commands (current and global namespaces), but for mutable variables it is quite strict: proc-local scope only unless you explicitly import vars. Many Functional Programming languages are lexically scoped, as is the lambda calculus. Inheritance in OO languages is a form of dynamic scoping. To illustrate, here are two versions of a lambda construct using dicts as environments -- one is statically scoped, the other is dynamically scoped:
# First, some general utilities: proc extend {env names values} { foreach n $names v $values {dict set env $n $v} return $env } proc capturelevel {{level 0}} { incr level set env [dict create] # Capture the environment of the caller: foreach varname [uplevel $level {info locals}] { upvar $level $varname var dict set env $varname $var } return $env } proc with {__env__ __body__} { dict with __env__ $__body__ } proc invoke {cmd args} {uplevel 1 $cmd $args} # Lexically-scoped lambda expressions: proc lambda-static {params body} { # Capture environment in constructor set env [capturelevel 1] list lambda-static: $params $body $env } proc lambda-static: {params body env args} { with [extend $env $params $args] $body } # Dynamically-scoped lambda expressions: proc lambda-dynamic {params body} { list lambda-dynamic: $params $body } proc lambda-dynamic: {params body args} { # Capture environment when invoked set env [capturelevel 1] with [extend $env $params $args] $body } # Invoke a lambda inside a dynamic environment with $a defined proc test {f a} {invoke $f}
A demo of lexical scoping:
proc make-lex a {lambda-static {} {puts "a = $a"}} set lambda [make-lex {Lexical scope}] test $lambda {Dynamic scope}
That should print Lexical scope because the value of $a in the environment of inner is bound at creation-time. Now, the same thing with dynamic scope:
proc make-dyn a {lambda-dynamic {} {puts "a = $a"}} set lambda [make-dyn {Lexical scope}] test $lambda {Dynamic scope}
Which prints Dynamic scope because the value of $a comes from the environment at the point at which we invoke the function.
escargo 2006-01-06: I have wondered whether it would be possible to add an explicit scope command where code could be executed in a (run-time) specified environment, a generalization of uplevel. There would need to be a way of specifying the environment (uplevel would be one way), but if we had continuations then we could use them as well.
NEM: Well, it depends what you mean by a scope. For instance, dict with in 8.5 provides a limited form of what you want (e.g., see my use of it above in the with procedure used for lambdas). It only captures variables, though, not commands. namespace eval provides another form of explicit scoping. Continuations are closures so they don't need a special scope command; you just apply the continuation. Something like the following would be very useful to me:
set scope [scope capture] scope with [scope extend $scope $names $values] $script
That could be nicely used to create lambdas etc. The capture, with, and extend sub-commands are given in my example of scoping above, all that would be needed (for a basic implementation) would be to put them in a namespace ensemble. Then, our lexical lambda (closure) is just:
proc lambda {params body} { set env [uplevel 1 {scope capture}] return [list lambda: $params $body $env] } proc lambda: {params body env args} { scope with [scope extend $env $params $args] $body }
Note that scope with has different semantics from dict with in that it creates a new local scope (or rather, restores an old local scope) rather than importing it into the current scope, and it also is side-effect-free. An extension would be to make scope capture capture more than just local scalar variables: arrays, commands, namespace scope etc.