ScopingRules
UNDER CONSTRUCTION
Scoping Rules
These are the rules and explanation of scope in the Euphoria Programming Language
What is SCOPE
Simply put, it's the part of a program that can be seen by other parts of the program. By default, identifiers can only be seen by code that exists in the same file as the declaration. However, this can be changed by the scope modifiers public, export, and global.
Module or File scope
Obviously, all identifiers are declared in a source code file. Each source code file can be thought of as a self-contained module of code. With this in mind, Euphoria not only allows identifiers to be declared outside of routines (much like most other programming languages), it also allows executable code to be written outside of routines. This code is executed in the order written when the program is run.
Identifiers declared outside of a routine, can be seen by all the code in the same file. These are said to have been declared at the file or module level. However, variables and constants can only be seen by code that follows the declaration, whereas routines can be seen by code before and after the routine's declaration.
Unless otherwise overridden by the public, export or global keywords, identifiers cannot be seen by code in other files.
Local or Routine scope
The only identifiers that can be declared inside a routine are the routine's arguments (if any) and variables. These are said to have been declared at the local or routine level. Such identifiers cannot be seen by any code outside of the routine that declares them. Note that this applies even to routines declared as global/public/export, such that even though these routines can be seen by code in other files, variables declared inside them are still hidden from all other code.
Scope for variables is from the declaration-assignment to the end of the file:
Scope for routines is for the entire file (or module):
Block scope
Euphoria allows variables to be declared inside block statements, such as if and loop constructs. These variables can only be seen by code that is also inside the same statement.
if varx = 1 then integer y = 0 -- Can only be seen by code in the same 'if' statement. while varx > varz do y += 1 varx = funcA(y, varxz) end while end if
Structures (routines, loops, conditionals) all enclose independent blocks of scope:
Global scope
The global keyword can be used when declaring constants, variables and routines at the file level. This causes the identifier to be visible to all other files used in the program.
global constant TRUE = 1 -- All files used in the program can see and use this identifier.
For example, Consider three files: ALPHA, BRAVO and CHARLIE. When CHARLIE declares 'x' as global and BRAVO includes CHARLIE, and ALPHA includes BRAVO. This means that code in BRAVO can see the 'x' declared in CHARLIE and so can code in ALPHA see the 'x' from CHARLIE.
Public scope
This scope modifier is very similar to export, with the only difference being how it is treated by the public include statement.
The public keyword can be used when declaring constants, variables and routines at the file level. This causes the identifier to be visible to only files that explicitly include the file containing the declaration. You would use this scope modifier for identifiers that are declared in one file but intended to be referenced by other files. The files containing public declarations are typically modules or libraries of code to be used by any application.
For example, Consider three files: ALPHA, BRAVO and CHARLIE. When CHARLIE declares 'x' as public and BRAVO includes CHARLIE, and ALPHA includes BRAVO. This means that code in BRAVO can see the 'x' declared in CHARLIE but code in ALPHA cannot see the 'x' from CHARLIE.
public constant TRUE = 1 -- Only files that explicitly include this file can see and use this identifier.
Export Scope
This scope modifier is very similar to public, with the only difference being how it is treated by the public include statement.
The export keyword can be used when declaring constants, variables and routines at the file level. This causes the identifier to be visible to only files that explicitly include the file containing the declaration. You would use this scope modifier for identifiers that are declared in one file but intended to be referenced by other files belonging to the same library. The files containing export declarations are typically sub-modules or sub-libraries of code to be used by a specific library and are not intended for direct usage by applications.
For example, Consider three files: ALPHA, BRAVO and CHARLIE. When CHARLIE declares 'x' as export and BRAVO includes CHARLIE, and ALPHA includes BRAVO. This means that code in BRAVO can see the 'x' declared in CHARLIE but code in ALPHA cannot see the 'x' from CHARLIE.
export constant TRUE = 1 -- Only files that explicitly include this file can see and use this identifier.
Public Include
Normally, files are included by another file by using the include statement. This allows the file doing the including to gain access to public and export declarations inside the file(s) being included. And if the including file is subsequently also included by a third file, the visibility of those public and export declarations are not propagated to that third file. This is done to avoid potential and inadvertent name clashes.
However, it is possible to use the public include statement instead of the simple include statement. When you do this, public but not export declarations are propagated to the third file. You would typically do this when writing a library module that includes another (maybe third party) library that you want the top level application to have access to, as if it was just are part of your library.
For example, you are writing a library that implements a database system. You want to use a third party library that does date-time arithmetic and allow users of your database library to also access the date-time routines.
-- My Database System -- public include datetime.e -- allows applications that include my database to see the public routines in datetime.e ...
If you used include datetime.e instead of public include datetime.e then applications that included your database library would not see the public declarations in the datetime.e file. And please note, only public and not export declarations are made visible this way. All export declarations can only be seen by explicitly including the file containing them.
Namespaces
One problem that can occur with scopes is that two or more independently written modules can declare identifiers with the same name. This is not an actual problem until your code tries to use one of them. When that happens, Euphoria informs you that there is a clash of names and that you have to resolve it by telling Euphoria exactly which of the clashing identifiers you actually want to use. This is done by adding a namespace qualifier to the identifier reference.
A namespace is just a name that is given to a library file. This is done in one of two ways. It can be embedded in a file as the file's first statement.
-- My Grand Database Library -- namespace granddb ...
or you can assign a namespace name to a file on the include statement. Doing this will override any embedded namespace name.
include granddb.e as dblib ...
Now, if there is a name clash with an identifier in the database library you can resolve the clash by prefixing the clashing name's reference with a namespace name.
if dblib:GOOD_RESULT then -- Use the constant GOOD_RESULT that's in the dblib file ...
Note: There is a predefined namespace called 'eu'. This is reserved for Euphoria's built-in routines and can be used if an included library declares a routine with the same name as one of Euphoria's built-in routines.
include somelib.e as SL if SL:equal(a,b) = eu:equal(a,b) then ...