Clever Geek Handbook
📜 ⬆️ ⬇️

Area of ​​visibility

A scope in English is a part of a program , within which an identifier declared as the name of a program entity (usually a variable , data type, or function ) remains associated with that entity, that is, it allows you to refer to it through yourself. They say that the object identifier is “visible” in a certain place of the program, if in a given place it is possible to refer to this object. Outside scope, the same identifier can be associated with another variable or function, or be free (not associated with any of them). The scope can, but is not required to, coincide with the scope of the object with which the name is associated.

Identifier binding ( English. Binding ) in the terminology of some programming languages - the process of defining a program object, access to which gives the identifier in a particular place of the program and at a particular point in its execution. This concept is essentially synonymous with scope , but may be more convenient when considering certain aspects of program execution.

A scope can also make sense for markup languages : for example, in HTML, the scope of a control's name is a form (HTML) from <form> to </ form> [1] .

Content

Types of scope

In a monolithic (one module) program without nested functions and without using OOP, there can be only two types of scope: global and local. Other types exist only if there are certain syntactic mechanisms in the language.

  • Global scope - the identifier is available in the whole text of the program (in many languages ​​the restriction is valid only in the text that appears after the declaration of this identifier).
  • Local scope - the identifier is available only within a certain function (procedure).
  • Visibility within a module can exist in modular programs consisting of several separate code fragments, usually located in different files. An identifier whose scope is a module is accessible from any code within the module.
  • Package or namespace . In the global scope, a named subdomain is artificially distinguished. The name "binds" to this part of the program and exists only inside it. Outside this area, the name is either not available at all or is limited.

In the OOP language, in addition to the above, special scope constraints can be maintained, valid only for members of classes (identifiers declared inside the class or related to it:

  • Private (private, closed) ( English private ) scope means that the name is available only within the methods of its class .
  • A protected scope means that a name is available only within its class and its descendant classes.
  • Common ( eng. Public ) scope means that the name is available within the scope to which its class belongs.

Ways to set scope

In the simplest cases, the scope is determined by the place of the declaration of the identifier. In cases when the place of the announcement cannot unambiguously determine the scope, special clarifications are applied.

  • An identifier declared outside of any function definition, procedure, type, is global.
  • An identifier declared inside a function definition is local to this function, that is, its scope is this function.
  • The identifier, which is part of the data type definition, in the absence of additional refinements, has the same scope as the type identifier in which it is included.
  • In languages ​​that support modules, packages, or namespaces, an identifier declared outside of all procedures and classes refers, by default, to the module, package, or namespace within which its declaration is located. The scope itself for a package or namespace is specified using special descriptions, and the modular scope is usually limited to the current source code file. A feature of this type of visibility is that the language, as a rule, contains tools that allow you to make the identifier accessible and outside its module (package or namespace), that is, to “expand” its scope. For this, there must be a combination of two factors: the module containing the identifier must be imported using a special command where it is supposed to be used, and the identifier itself when describing it must be additionally declared exported . The ways in which the identifier can be exported can be different. These can be special commands or modifiers in descriptions, naming conventions (for example, in the Go language, identifiers of a packet scope that begin with a capital letter are exported). In a number of languages, each module (package) is artificially divided into two parts: the definition section and the implementation section, which can be located within the same source code file (for example, in Delphi) or in different (for example, in Modula-2 language) ; exporters are identifiers declared in the definition module.
  • The scope of an identifier declared inside an OOP class is, by default, either private or shared. A different scope is given using a special description (for example, in C ++ these are modifiers private , public , protected ) [2] .

The above list does not exhaust all the nuances of scoping that may exist in a specific programming language. Thus, for example, various interpretations of combinations of modular scope and declared visibility of OOP class members are possible. In some languages ​​(for example, C ++), declaring a personal or protected scope for a class member restricts access to it from any code that is not related to the methods of its class. In others (Object Pascal), all members of the class, including personal and protected, are fully accessible within the module in which the class is declared, and the scope limitations apply only to other modules importing the given one.

Hierarchy and Ambiguity Resolution

The areas of visibility in the program naturally constitute a multi-level structure in which some areas are part of others. The hierarchy of areas is usually built at all or some levels from the set: “global — batch — modular — classes — local” (a particular order may differ somewhat in different languages).

Packages and namespaces can have several levels of nesting, respectively, and their scope will be nested. Relationship scopes of modules and classes can vary greatly in different languages. Local namespaces can also be nested, even if the language does not support nested functions and procedures. For example, in the C ++ language, there are no nested functions, but each compound statement (containing a set of commands enclosed in curly brackets) forms its own local scope in which it is possible to declare its variables.

The hierarchical structure allows to resolve ambiguities that arise when the same identifier is used in the program in more than one value. The search for the desired object always begins with the scope in which the code referring to the identifier is located. If an object with the necessary identifier is in this scope, then it is this object that is used. If there is none, the translator continues to search among the identifiers visible in the ambient scope, if it is not there, in the next level of hierarchy.

  program Example1 ;
 var 
   a , b , c : Integer ;  (* Global Variables. *)

   procedure f1 ;
   var b , c : Integer (* Local variables of the procedure f1. *)
   begin
     a : = 10 ;  (* Changes global a. *)
     b : = 20 ;  (* Changes the local b. *)
     c : = 30 ;  (* Changes the local s. *)
     writeln ( '4:' , a , ',' , b , ',' , c ) ;
   end ;

   procedure f2 ;
   var b , c : Integer (* Local procedure variables f2. *)
     procedure f21 ;
     var c : Integer (* Local procedure variable f21. *)
     begin
       a : = 1000 ;  (* Changes global a. *)
       b : = 2000 ;  (* Modifies the local b of the f2 procedure. *)
       c : = 3000 ;  (* Changes the local c of the procedure f21. *)
       writeln ( '5:' , a , ',' , b , ',' , c ) ;
     end ;
   begin
     a : = 100 ;  (* Changes global a. *)
     b : = 200 ;  (* Changes the local b. *)
     c : = 300 ;  (* Changes the local c. *)
     writeln ( '6:' , a , ',' , b , ',' , c ) ;
     f21 ;
     writeln ( '7:' , a , ',' , b , ',' , c ) ;
   end ;
 begin
   (* Initializing global variables. *)
   a : = 1 ; 
   b : = 2 ;
   c : = 3 ;
   writeln ( '1:' , a , ',' , b , ',' , c ) ;
   f1 ;
   writeln ( '2:' , a , ',' , b , ',' , c ) ;
   f2 ;
   writeln ( '3:' , a , ',' , b , ',' , c ) ;
 end .

So, when you run the above program in Pascal, the following output will be obtained:

  1: 1,2,3       
  4: 10,20,30
  2: 10,2,3      
  6: 100,200,300
  5: 1,000,2000,3000
  7: 1000,2000,300
  3: 1000,2,3    

In the f1 function, the variables b and c are in the local scope, so their changes do not affect the global variables of the same name. The f21 function contains in its local scope only the variable c , so it changes both the global a and b local in the ambient function f2 .

Lexical vs. dynamic scopes

The use of local variables — having limited scope and existing only within the current function — helps to avoid name conflicts between two variables with the same name. However, there are two very different approaches to the question of what it means to “be inside” of a function and, accordingly, two options for implementing local scope:

  • lexical scope , or lexical context ( eng. lexical scope ), or lexical (static) binding ( eng. lexical (static) binding ): the local scope of the function is limited to the text of the definition of this function outside of it).
  • dynamic scope , or dynamic context ( born dynamic scope ), or dynamic binding ( born dynamic binding ): local scope is limited to the execution time of the function (the name is available while the function is executed and disappears when the function returns control to the code that caused it) .

For "pure" functions that operate only with their own parameters and local variables, the lexical and dynamic scope are always the same. Problems arise when a function uses external names, for example, global variables or local variables of the functions in which it is included or from which it is called. So, if a function f calls a function g not embedded in it, then with the lexical approach, the function g does not have access to the local variables of the function f . With the dynamic approach, the function g will have access to the local variables of the function f , since g was called during the operation of f .

For example, consider the following program:

  x = 1
 function g () { echo $ x ;  x = 2 ;  }
 function f () { local x = 3 ;  g ;  }
 f # will output 1 or 3?
 echo $ x # will print 1 or 2?

The g() function outputs and changes the value of the variable x , but this variable is not in g() neither a parameter nor a local variable, that is, it must be associated with a value from the scope in which g() belongs. If the language in which the program is written uses lexical scopes, then the name «x» inside g() must be associated with the global variable x . The function g() , called from f() , displays the initial value of the global х , then changes it, and the modified value is output from the last line of the program. That is, the program will first output 1, then 2. Changes to the local x in the text of the f() function will not affect this output, since this variable is not visible either in the global area or in the g() function.

If the language uses dynamic scopes, then the name «x» inside g() associated with the local variable x function f() , since g() is called from inside f() and is included in its scope. Here, the g() function will output the local variable x the f() function and change it, but this will not affect the global x value, so the program will first output 3, then 1. Because in this case the program is written in bash , which uses dynamic approach, in reality, this is exactly what will happen.

Both lexical and dynamic linking have their positive and negative sides. Practically, the choice between the one and the other is made by the developer based on both his own preferences and the nature of the designed programming language. Most of the typical imperative high-level languages, originally designed to use the compiler (in the target platform code or in the virtual machine bytecode, it doesn’t matter) implement the static (lexical) scope, since it is more conveniently implemented in the compiler. The compiler works with a lexical context that is static and does not change when the program is executed, and by processing the name call, it can easily determine the address in memory where the object associated with the name is located. The dynamic context is not available to the compiler (since it can change during the execution of the program, because the same function can be called in many places, and not always explicitly), so to ensure dynamic scope, the compiler must add dynamic support to the object definition to the code, referenced by id. This is possible, but reduces the speed of the program, requires additional memory and complicates the compiler.

In the case of interpreted languages ​​(for example, scripting ), the situation is fundamentally different. The interpreter processes the program text at the moment of execution and contains the internal structures of support for execution, including tables of variable names and functions with real values ​​and addresses of objects. It is easier and faster for the interpreter to perform dynamic linking (by a simple linear search in the table of identifiers) than to constantly monitor the lexical scope. Therefore, interpreted languages ​​more often support dynamic name binding.

Name binding features

Within the framework of both a dynamic and lexical approach to the binding of names, there may be nuances associated with the peculiarities of a particular programming language or even its implementation. As an example, consider two C-like programming languages: JavaScript and Go . Languages ​​are syntactically quite close and both use lexical scope, but, nevertheless, differ in the details of its implementation.

Beginning of the local name scope

The following example shows two textually similar code snippets in JavaScript and Go. In both cases, the global scope is declared the scope variable, initialized with the string "global", and in the f() function, the scope value is first output, then the local declaration of the variable with the same name, initialized with the string "local", and finally repeated outputting the scope value. The following is the actual result of executing the f() function in each case.

JavascriptGo
  var scope = "global" ; 
 function f () {
     alert ( scope );  //?
     var scope = "local" ;
     alert ( scope );   
 }
  var scope = "global"
 func f () {
	 fmt .  Println ( scope ) //?
	 var scope = "local"
	 fmt .  Println ( scope )
 }
undefined
local
global
local

It is easy to see that the difference lies in what value is displayed in the line marked with a comment with a question mark.

  • In JavaScript, the scope of a local variable is the entire function , including that part that is before the declaration; however, initialization of this variable is performed only at the moment of processing the line where it is located. At the time of the first alert(scope) call, the local variable scope already exists and is available, but has not yet received a value, that is, according to the rules of the language, it has a special value undefined . That is why “undefined” will be displayed in the marked line.
  • Go uses a more traditional approach for this type of language, according to which the name scope begins with the line where it is declared. Therefore, inside the f() function, but before declaring a local variable scope this variable is not available, and the command marked with a question mark displays the value of the global variable scope , that is, “global”.

Block visibility

Another nuance in the semantics of lexical scope is the presence or absence of the so-called “block visibility”, that is, the ability to declare a local variable not only within a function, procedure or module, but also within a separate command block (in C-like languages ​​it is enclosed in curly brackets {} ). The following is an example of an identical code in two languages ​​that gives different results from the execution of the f() function.

JavascriptGo
  function f () { 
	 var x = 3 ; 
	 alert ( x );
	 for ( var i = 10 ; i < 30 ; i + = 10 ) {
		 var x = i ;
     	 alert ( x );
	 }
	 alert ( x );  //?
 }
  func f () {
	 var x = 3
	 fmt .  Println ( x )
	 for i : = 10 ;  i < 30 ;  i + = 10 {
		 var x = i
		 fmt .  Println ( x )
	 }
	 fmt .  Println ( x ) //?
 }
3
ten
20
20
3
ten
20
3

The difference is manifested in what value will be displayed by the last operator in the f() function, marked with a question mark in the comment.

  • In JavaScript, there is no block scope, and the repeated declaration of a local variable works simply as a normal assignment. Assigning x values ​​to i inside a for loop changes the only local variable x that was declared at the beginning of the function. Therefore, after the loop ends, the variable x saves the last value assigned to it in the loop. This value is displayed as a result.
  • In Go, a block of operators forms a local scope, and the variable x declared inside the loop is a new variable, the scope of which is only the body of the loop; it overlaps the x declared at the beginning of the function. This “twice local” variable gets a new value in each pass of the loop and is output, but its changes do not affect the variable x declared outside the loop. After the end of the cycle, the variable х declared in it ceases to exist, and the first x becomes visible again. Its value remains the same, it is displayed as a result.

Visibility and Existence of Objects

You should not identify the visibility of an identifier with the existence of the value with which this identifier is associated. The ratio of the visibility of the name and the existence of an object is affected by the logic of the program and the class of memory of the object. Further some typical examples.

  • For variables, the memory for which is allocated and released dynamically (in a heap ), any relationship between visibility and existence is possible. The variable can be declared and then initialized, then the object corresponding to the name will actually appear after entering the scope. But an object can be created in advance, saved, and then assigned to a variable, that is, appear earlier.
  • For local variables with a static memory class (in C and C ++ languages), the value appears (logically) at the time the program is started. In this case, the name is in scope only when executing the function containing it. Moreover, in the intervals between functions, the value is preserved.
  • Automatic (in C terminology) variables created at the entrance to a function and destroyed at the exit exist during the period of time when their name is visible. That is, for them, the times of availability and existence can practically be considered coincident.


Examples

C

  // Starts the global scope.
 int countOfUser = 0 ;

 int main ()
 {
     // From this point on, a new scope is declared, in which the global one is visible.
     int userNumber [ 10 ];
 }
  #include <stdio.h> 
 int a = 0 ;  // global variable

 int main ()
 {
     printf ( "% d" , a );  // the number 0 will be displayed
     {
        int a = 1 ;  // local variable a is declared, global variable a is not visible
        printf ( "% d" , a );  // the number 1 will be displayed
        {
           int a = 2 ;  // another local variable in the block, the global variable a is not visible, the previous local variable is not visible either
           printf ( "% d" , a );  // the number 2 will be displayed
        }
     }
 }

Notes

  1. ↑ HTML language specification , translator: A. Piramidin, intuit.ru, ISBN 978-5-94774-648-8 , 17. Lecture: Forms.
  2. ↑ Scopes (Undefined) . The date of circulation is March 11, 2013. Archived March 16, 2013.
Source - https://ru.wikipedia.org/w/index.php?title=Visibility_&oldid=97357407


More articles:

  • Raymond Arroyo
  • Husa Cardonik
  • Long, Wolf Gitmanovich
  • Russian image (magazine)
  • Levasseur PL.10
  • Vershinin, Konstantin Andreevich
  • Shiryaev, Vyacheslav Vladimirovich
  • Bekan (Chad)
  • Mountain Tatarintsevo
  • Kandelaki, Valerian Lazarevich

All articles

Clever Geek | 2019