'''Scope''' refers to the visibility of various contexts when [binding] names of [variable%|%variables] to [EIAS%|%values], or when resolving [command] names. ** Description ** A name is considered '''[http://en.wikipedia.org/wiki/Lexical_scoping%|%lexically-scoped]''' ('''statically-scoped''') when the object it refers to corresponds to the static layout of the program text, and '''[http://en.wikipedia.org/wiki/Scope_%28computer_science%29#Lexical_scope_vs._dynamic_scope%|%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 [namespace%|%namespaces] have a more lexical flavour. [namespace%|%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 [command%|%Commands] and non-local [variable%|%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: A '''[namespace]''': Each namespace is located somewhere in the namespace hierarchy, and actually provides two namespaces, one for variables and one for commands. `[variable]` references a namespace variable relative to the namespace in which it is invoked, and `[proc]` creates a command in the namespace within which it is invoked. Therefore, both variables and commands have a `lexically-scoped` flavour. The '''[global] [namespace]''': The namespace at the top of the hierarchy, and the only namespace that has as its name the [empty string]. It is the namespace associated with the top-level call frame, as accessed via `[uplevel] #0`. `[global]` references the global namespace. '''call frame''': The local variable scope of an invoked command is a component of its call frame. '''hidden''': a flat namespace for commands; used by `[interp hide]` and `[interp invokehidden]`. 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: '''channel''': the name of a channel as understood by commands such as `[chan]`, `[puts]`, `[gets]`, `[read]`, and `[close]`. '''interp''': the name of an interpreter as understood by the `[interp]` subcommands. ---- [RS]: '''dynamic scope''': determines variable value at runtime - used in older [Lisp] versions '''lexical scope''': conserves the value of variables they had at the time a [proc] was defined, for instance in [closures] - considered more advanced in [Lisp] circles. [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 [dict]s 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 [continuation]s 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. [Continuation]s 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 [lambda%|%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. ** See Also ** [C-like structs and file scope]: [Tcl_Obj vs Command]: <> Concept