Scope is a context within which to resolve names, [binding] names to values. Tcl is capable of both lexical and dynamic scoping. Tcl requires lexical scope for command resolution, and prefers (or defaults to) dynamic scope for variable resolution within [[[proc]]]. Tcl has partitioned static scopes - a different binding can exist in each static scope for variables and commands with the same name. Tcl has many scopes: '''namespace''' is an hierarchal scope for binding the names of commands and variables. [[[variable]]] references a variable name in some namespace scope relative to the scope in which it is invoked, and [[[proc]]] creates a command in the namespace scope within which it is invoked. For this reason, '''namespace scope''' can be considered a lexical scope. Additionally, namespace scope is partitioned into distinct bindings for variable and command names. '''global''' scope is a distinguished namespace scope, equivalent to the namespace '''::''' in some interpreter. '''global''' scope is also a distinguished call frame scope - equivalent to [upvar] #0. Global scope can be referenced by the [[[global]]] command. '''hidden''' scope is a per-interpreter scope for [interp] hidden commands. '''call frame''' scope is the context of some call frame in the current evaluation (accessible via [uplevel]). '''proc local''' scope is the context within which all otherwise unqualified variable names referenced in a [proc] are defined. Proc local is a special case of a call frame scope, being the nearest enclosing call frame scope. '''channel''' scope is the per-interpreter global scope for resolving channel names e.g. stdin, file5. There is no eponymous command associated with channels (but it'd be easy enough to fudge.) '''interp''' scope is an hierarchal global scope for resolving interpreters, not to be confused with the use of the command namespace for resolving the eponymous command associated with each interpreter. '''package''' scope is a global for naming packages. ---- In summary, there are two major kinds of scoping in tcl - dynamic and lexical (or static) scope. '''dynamic''' scope is accessed by default inside a proc, or by means of [upvar] with unqualified names, and is preferred for variable name resolution. '''static''' or '''lexical''' scope is accessed explicitly by namespace encoded names, the [global] operator, and implicitly by [command invocation] and by the [variable] operator. Command resolution prefers dynamic scope. [RS] has learnt this distinction, however: * '''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", as 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", as the value of $a comes from the environment at the point at which we invoke the function. ---- '''Some Dichotomies that Need Explaining''' [Variable]s ([binding]s from name to [Tcl_Obj] value) can be referenced by static and dynamic scopes. [Proc]s and [Command]s can only be referenced by static scopes. Why is this? Much of the effort in considering and implementing [lambda] have to do with avoiding the consequences of this dichotomy. (''[NEM] -- what about [uplevel]?'') The differences and similarities between [Tcl_Obj]s and [Command]s is a fundamental and interesting topic. [Tcl_Obj vs Command] is a page to discuss it. - on reflection that page is misgiven [CMcC] 20041015 ''[escargo] 5 Jan 2006'' - 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 generalizeation 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'' provides a limited form of what you want in 8.5 (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]s 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. ---- [Category Concept]