Friday, April 10, 2020

Scoping in Visual FoxPro – Part 1

One of the issues that seems to cause endless confusion is that of the scope of properties, methods and variables in Visual FoxPro. In fact it is really quite simple, but can be confusing because of the differences in syntax and meaning. Let’s start, this week, with variables and we’ll discuss properties and methods next week.
Variable Scoping
The scope of a variable defines how long it exists and which programs, methods, procedures, or User Defined Functions (UDFs) have access to it. Just for the purpose of discussing scope, let’s think of the term “procedure” as including programs, methods, procedures, and user-defined functions. With this in mind, there are three levels of scope:
Public Variables
Also known as “Global” – in fact the recommended scope identifier for a public variable is “g” for this reason (the letter “p” being reserved for “private”).  A public variable is available to an entire application irrespective of where, or when it is created. It remains extant until it is specifically RELEASEd by name or the application  (or VFP itself) is closed. This means that any program can read, or change, the value of the variable at any time. While there are some, very specialized situations in which a Public variable may be the only solution, as a general rule their use in program code is not recommended – they are simply too vulnerable to unexpected change to be very reliable.
Having said that, it is important to realize that all variables created at the command window are Public in scope – and this is one cause of the oft-heard cry that “it works in the command window, but not in the exe”. However this is the exception. In all other circumstances, a public variable must be explicitly declared before it is assigned a value and failing to do so will generate an error. This is why you will usually see a PUBLIC preceded with a RELEASE command (see code snippet below). Executing the PUBLIC command physically creates the specified variable(s) and initializes them to .F.
*** Declare a couple of Public Variables
RELEASE gnPubNum, gcPubStr
PUBLIC gnPubNum, gcPubStr
*** Until actually assigned a value PUBLIC variables have the value .F.
gnPubNum = 100
gcPubStr = “test string”
Private Variables
A private is available to the procedure that created it and any procedures called by the creator, or called in a chain of procedures initiated by the creator. When the creating procedure ends, the variable is automatically released. If no other scope has been specified when a variable is assigned a value, Visual FoxPro implements it as a private variable. This means that all that is necessary to create a private variable is to assign a value to it:
*** Create the following variables as PRIVATE
pnPriNum = 100
pcPriStr = “Private String”
However, there is a wrinkle to beware of here. If the variable name is already in scope (i.e. it has already been declared as Public, or as Private in a parent procedure) the assignment statements above do NOT create new variables, they simply change the value of the existing variable of the same name. This is not always what is wanted and can be very hard to trace when it causes problems. Consider the following code:
*** Run the child procedure 3 times
FOR lnCnt = 1 TO 3
  CallChild( lnCnt * 4 )
NEXT

*** Here is the child procedure definition
CallChild( tnCounter )
FOR lnCnt = 1 TO tncounter
  ? “Loop Number: “ + TRANSFORM( lnCnt )
NEXT
Now ask yourself, how many times will the child procedure execute? The answer is, of course, once! The same loop counter name (lnCnt) is being used in both the parent and child procedures, with the result that after the first call to the child procedure the value will be 4 (i.e. lnCnt = 1 * 4 ) and so the parent loop immediately exits. This is a pretty obvious example of how things can go wrong with Private variables – imagine if, instead of a couple of lines below the first, the second use of lnCnt was in a different program file executed three or four levels down in the calling chain! Debugging what was going wrong would be a nightmare!
Fortunately the use of the PRIVATE command gets around these issues for us. However, unlike the PUBLIC command it does not immediately create a new variable. Instead it tells Visual FoxPro that when (and if) a value is assigned to the specified variable name, that it must ignore any existing variables and create a new one for which the currently executing procedure is the owner. If you re-use an existing variable name (as I did in the example above) but declare it PRIVATE before assigning a value and then look in the debugger you will see that VFP still has the original variable but it is now “hidden” and a new one has been created.
Note: The PARAMETERS statement is equivalent to PRIVATE. Variables defined using this command are created as if they had been declared explicitly as private and any variables with the same names that are in scope are hidden.
Local Variables
Local variables, like private variables are scoped to the procedure that creates them and are released as soon as that procedure terminates. However, unlike private variables, they have no visibility outside of their parent procedure and must be explicitly declared using the LOCAL command. When the LOCAL command is executed a new variable is immediately created, and initialized to .F. (contrast this with the PRIVATE command which does not actually create a variable).
The only way in which a local variable can be modified outside of its owning procedure is if it is specifically passed by reference. Thus:
*** Create a local variable
LOCAL lnVar
? lnVar && .F.
lnVar = 100
? lnVar && 100
*** Call function and pass by value
func1( lnVar )
? lnVar && Still  100
*** Call function and pass by reference
func1( @lnVar )
? lnVar  && Now it is changed!!!

FUNCTION func1( tnValue )
tnValue = 250
RETURN
Visual FoxPro variables are “weakly typed”. This means that even though a variable may be assigned a specific value, it is not restricted to holding only values of that specific data type. In short, variables in Visual FoxPro are chameleon-like in their ability to adopt the color of their background. The following code is perfectly legitimate in Visual FoxPro although it most certainly would not be allowed in any strongly-typed language (The result of running this series of commands will be a 6 element array named “lunknown” with today’s date stored in each element):
lunknown = “Andy and Marcia”
lunknown = 10
DIMENSION lunknown[ 3, 2]
lunknown = DATE()
Weak typing is somewhat of a mixed blessing. Since Visual FoxPro is a data-oriented language, it makes sense to allow variables (especially arrays) to be weakly typed so that data can be transferred easily between tables and memory. However, it does require a more disciplined approach on the part of developers to avoid confusion when working on code. It is strongly recommended that you get into the habit of explicitly declaring the variables you use and that you also adopt a naming convention. The most commonly used convention is one in which the first two characters of a variable’s name indicate it’s scope and intended data type, as shown below:

Scope Identifier
Used For
Type Identifier
Used for
g
Public (= Global)
c
Character
p
Private
n
Numeric (may be either Decimal or Integer)


(i, f, d, b)*
Special Numeric (Integer, Float, Double, Binary)
l
Local
d
Date
t
Parameter (Private or Local)
t
DateTime


l
Logical (Boolean)


y
Currency


(m)**
Memo


o
Object Reference


a
Array (Mixed Type)


u
Unspecified (Unknown or Variable)
      * VFP does not, internally, distinguish between these types – all are implemented as “numbers”. However, with data sources that do (e.g. SQL Server) it is useful to identify the original type
      ** Although not strictly a data type, it is often useful to indicate that specific variable is intended to hold the contents of a memo field

Published Wednesday, May 04, 2005 4:23 AM by andykr

No comments:

Post a Comment

Writing better code (Part 1)

Writing better code (Part 1) As we all know, Visual FoxPro provides an extremely rich and varied development environment but sometimes to...