Scope
A symbol’s scope is the portion of the program where that symbol’s
declaration is in effect, i.e. where that symbol is visible.
In a Phix every variable must be declared before use, and a program can be read from beginning to end without encountering any variables (or constants) that get defined later.
You can explicitly declare a forward routine that will be defined later, or just invoke it implicitly. Most of the builtin routines, excepting those with an include statement in the Definition section, are in fact effectively declared as explicit forward global routines, in psym.e/syminit(), and a similar thing occurs for pwa/p2js via p2js_keywords.e, however note that transpilation generally requires explicit over implicit (more notes below).
Implicit forward routine calls are assumed to be local and generate a compilation error if still unresolved at the end of file. If the routine is actually global (and not a typo) there must be an explicit forward global routine statement, and that must occur before the first (implicit) call. If the compiler can unambiguously resolve an implicit local routine to a global routine, it will, but at the same time also issue a warning.
Procedures, functions and types can call themselves recursively. Mutual recursion, where routine A calls routine B which [in]directly calls routine A, is fully supported.
It is also possible to call a routine that comes later in the source using the special functions routine_id(), and either call_func() or call_proc(), see Library Routines - Dynamic Calls. This mechanism was the only means of making a forward call in RDS Eu, and may therefore be quite prevalent in legacy code.
A symbol is defined from the point where it is declared to the end of its scope.
The scope of a variable declared inside a procedure or function (a parameter or private variable) ends at the end of the procedure or function.
The controlling variable used in a for-loop is special: if a variable of that name has not been declared in the immediately surrounding scope, it is automatically declared at the beginning of the loop, and its scope ends at the end of the for-loop. (Be warned there may be some cases where new/reuse is ill-defined/an implementation detail, eg nested functions.)
The scope of all other variables, constants, procedures, functions and types ends at the end of the source file in which they are declared and they are referred to as local, unless the keyword global precedes their declaration, in which case their scope extends indefinitely.
You can use the same name in many different for-loops as long as the loops are not nested. You do not have to declare loop variables as you would other variables. However, since Phix does not support floating point for loops, should you choose to, use the type integer.
When you include a Phix file in a main file (see Special Top-Level Statements), only the variables and routines declared using the global keyword are accessible or even visible to the main file. The other, non-global, declarations in the included file are forgotten at the end of that file, and you will get a "not declared" error message, should you try to use them in the main file.
The global keyword makes it clear which symbols can be used externally, and which symbols are strictly for internal use within the include file.
This information may be helpful when maintaining or enhancing the file, or when learning how to use the file.
Optionally the local keyword can be used as the logical counterpart to global, but it does not actually change anything, it is for clarification of intent only, and noting that local means file rather than routine level, and there is no similar private (compatability) alias like there is for public (see next).
Phix encourages you to restrict the scope of symbols. If all symbols were automatically global to the whole program, you might experience naming conflicts, especially in a large program consisting of files written by many different programmers. A naming conflict might cause a compiler error message, or it could lead to a very subtle bug, where different parts of a program accidentally modify the same variable without being aware of it. Try to use the most restrictive scope that you can. Make variables private to one routine where possible, and where that is not possible, make them local to a file, rather than global to the whole program.
When Phix looks up the declaration of a symbol, it first checks the current routine, then the current file, then globals in other files. At least that is the simple version, see the comments in psym.e for more detailed technical information. Symbols that are more localized may temporarily override (aka shadow) symbols that are more globalized, without any error message occurring.
Constant declarations must be outside of any subroutine.
Constants can be global or local, but not private.
Update: no longer true, this part of the docs needs rewriting... See constants.
Historically, variable declarations inside a subroutine had to appear at the beginning, before the executable statements of the subroutine. However block scope is now supported, meaning a variable can be private to a single branch of an if-construct, for example, and they can now be mixed in with executable statements as needed.
Note however that block scopes may not "shadow" containing local scopes: attempts to declare another variable "i", for instance, when one is already visible from some containing block or routine scope will trigger a compilation error.
Likewise, declarations at the top level, outside of any subroutine, originally had to be at the very top, but can now be nested inside a loop or if-statement, and similarly cannot shadow or otherwise (temporarily) override any other locally defined containing block or file-level variables, however anything locally declared can shadow any (same-named) globals defined in another source file.
In a Phix every variable must be declared before use, and a program can be read from beginning to end without encountering any variables (or constants) that get defined later.
You can explicitly declare a forward routine that will be defined later, or just invoke it implicitly. Most of the builtin routines, excepting those with an include statement in the Definition section, are in fact effectively declared as explicit forward global routines, in psym.e/syminit(), and a similar thing occurs for pwa/p2js via p2js_keywords.e, however note that transpilation generally requires explicit over implicit (more notes below).
Implicit forward routine calls are assumed to be local and generate a compilation error if still unresolved at the end of file. If the routine is actually global (and not a typo) there must be an explicit forward global routine statement, and that must occur before the first (implicit) call. If the compiler can unambiguously resolve an implicit local routine to a global routine, it will, but at the same time also issue a warning.
Procedures, functions and types can call themselves recursively. Mutual recursion, where routine A calls routine B which [in]directly calls routine A, is fully supported.
It is also possible to call a routine that comes later in the source using the special functions routine_id(), and either call_func() or call_proc(), see Library Routines - Dynamic Calls. This mechanism was the only means of making a forward call in RDS Eu, and may therefore be quite prevalent in legacy code.
A symbol is defined from the point where it is declared to the end of its scope.
The scope of a variable declared inside a procedure or function (a parameter or private variable) ends at the end of the procedure or function.
The controlling variable used in a for-loop is special: if a variable of that name has not been declared in the immediately surrounding scope, it is automatically declared at the beginning of the loop, and its scope ends at the end of the for-loop. (Be warned there may be some cases where new/reuse is ill-defined/an implementation detail, eg nested functions.)
The scope of all other variables, constants, procedures, functions and types ends at the end of the source file in which they are declared and they are referred to as local, unless the keyword global precedes their declaration, in which case their scope extends indefinitely.
You can use the same name in many different for-loops as long as the loops are not nested. You do not have to declare loop variables as you would other variables. However, since Phix does not support floating point for loops, should you choose to, use the type integer.
When you include a Phix file in a main file (see Special Top-Level Statements), only the variables and routines declared using the global keyword are accessible or even visible to the main file. The other, non-global, declarations in the included file are forgotten at the end of that file, and you will get a "not declared" error message, should you try to use them in the main file.
The global keyword makes it clear which symbols can be used externally, and which symbols are strictly for internal use within the include file.
This information may be helpful when maintaining or enhancing the file, or when learning how to use the file.
Optionally the local keyword can be used as the logical counterpart to global, but it does not actually change anything, it is for clarification of intent only, and noting that local means file rather than routine level, and there is no similar private (compatability) alias like there is for public (see next).
Compatibility Note: Euphoria has public and export qualifiers as well as global. Phix simply treats all three identically, except for export in the top-level file when building a .dll or .so. The "priorities" handling as documented in psym.e addresses pretty much the same concerns (that Euphoria uses global/public/export to distinguish between) but in a completely different manner.Sometimes, when using include files developed by others, you may encounter a naming conflict. One of the include file authors has used the same name for a global symbol as one of the other authors. Since you have the source code you could simply edit one of the include files to correct the problem, but then you would have repeat this process whenever a new version of the include file was released. Phix has a simpler way to solve this, albeit one that is not p2js-compatible. Using an extension to the include statement, you can say for example:
JavaScript is essentially a function-scoped language, with block scope added as a bit of an afterthought, hence most of these rules do not strictly apply when using pwa/p2js to transpile code so it can be run in a browser. In particular everything declared at file level is effectively global, and there are no namespaces at all. Otherwise however these rules can still be helpful when initially developing on the desktop.
include johns_file.e as john include bills_file.e as bill john:x += 1 bill:x += 2In this case, the variable x was declared in two different files, and you want to refer to both variables in your file. Using the namespace identifier of either john or bill, you can prefix x to indicate which one you are referring to. We sometimes say that john refers to one namespace, while bill refers to another distinct namespace. You can attach a namespace identifier to any user-defined variable, constant, procedure or function. You can do it to solve a conflict, or simply to make things clearer. A namespace identifier has local scope. It is known only within the file that declares it, i.e. the file that contains the include statement. Different files might define different namespace identifiers to refer to the same included file. See also include.
Phix encourages you to restrict the scope of symbols. If all symbols were automatically global to the whole program, you might experience naming conflicts, especially in a large program consisting of files written by many different programmers. A naming conflict might cause a compiler error message, or it could lead to a very subtle bug, where different parts of a program accidentally modify the same variable without being aware of it. Try to use the most restrictive scope that you can. Make variables private to one routine where possible, and where that is not possible, make them local to a file, rather than global to the whole program.
When Phix looks up the declaration of a symbol, it first checks the current routine, then the current file, then globals in other files. At least that is the simple version, see the comments in psym.e for more detailed technical information. Symbols that are more localized may temporarily override (aka shadow) symbols that are more globalized, without any error message occurring.
Constant declarations must be outside of any subroutine.
Constants can be global or local, but not private.
Update: no longer true, this part of the docs needs rewriting... See constants.
Historically, variable declarations inside a subroutine had to appear at the beginning, before the executable statements of the subroutine. However block scope is now supported, meaning a variable can be private to a single branch of an if-construct, for example, and they can now be mixed in with executable statements as needed.
Note however that block scopes may not "shadow" containing local scopes: attempts to declare another variable "i", for instance, when one is already visible from some containing block or routine scope will trigger a compilation error.
Likewise, declarations at the top level, outside of any subroutine, originally had to be at the very top, but can now be nested inside a loop or if-statement, and similarly cannot shadow or otherwise (temporarily) override any other locally defined containing block or file-level variables, however anything locally declared can shadow any (same-named) globals defined in another source file.