One last thought is about how the private scope can be turned into a more controlled scope: You can control the visibility that makes up the scope of private variables.
First of all, let's look at what scope means in the context of variables. It's easiest to understand for LOCAL variables as it's such a straight forward name of a scope of visibility and availability. LOCAL vars are scoped local, there I said the obvious.
At the start of any code like a method, function, procedure or a whole prg, you
a) don't have any local variable
b) therefore only see global (PUBLIC) variables and PRIVATE ones, defined
before this code was called.
And to state something else very obviously a) doe not only means you don't have any LOCAL vars for yourself, now, you first have to declare them, it also means all the local vars of other code that's still on the call stack - I come back to what this is nd means later - is hidden from you. They are not released, they still exist, they are just not available to you. So this introduces the aspect of hiding variables and protecting them from access of any code not eligible to access them.
I already touched the other well understandable scope, the PUBLIC scope, in contrast to LOCALs all PUBLIC vars are not hidden, you can access them (and that includes assigning values to them) anywhere in any code, until you actively RELEASE them. That's also a notable difference, both LOCAL and PRIVATE vars are passively RELEASED when their scope ends, you don't ever need to write a RELEASE command for them (but you can, if you want to RELEASE a local var earlier than it is done automatically).
What you can actively do when you don't want your code to be effected by private vars you don't expect and want to use, is starting your code with
This should also show that PRIVATE isn't like LOCAL variable declaration command, PRIVATE is a command that perhaps would have better been called HIDE, it just means to hide whatever private variable you specify, if they exist, and so PRIVATE ALL means HIDE ALL private vars that exist at this moment.
This will effectively mean that you hinder PRIVATE vars to enter your own code scope, which does what? Stop reading and take a guess.
Well, you limit the scope of privates to only be visible to the code that created them and thus force them to be LOCAL. You waive your eligibility to access them.
The only thing that is you can't use PRIVATE ALL before LPARAMETERS or PARAMETERS. You can specify parameters in the PROCEDURE or FUNCTION declaration line of code in brackets after the function name and they become LOCAL variables that are set and initialised by received values, which has several mechanisms including "by reference", but that's not the discussion point.
There you have another explanation why PARAMETERS is necessary in the language, if you want to enable developers to decide to receive parameters into variables with PRIVATE and not LOCAL scope.
There's no reason to retroactively add PRIVATE ALL to all your existing code, though, because when you always declare your variables LOCAL you also at the same time hide private vars of the same name. Which means LOCAL does not just declare a variable with the name or names, it also hides private vars of the same name, it does not override them or delete or release them.
Doing both things, PRIVATE ALL and declare all variables LOCAL or with LPARAMETERS you double protect against any unwanted side effects of privates.
It also introduces the chance to exactly document that you want to actually use privates in their variable scope, if you extend it to PRIVATE ALL EXCEPT pcFoo, pcBar, for example. The only doesnide of it is more linguistically, as this statement sounds like you say every variable is private except the two ones and the opposite of that is what it does, it hides all private variables but allows access to pcFoo and pcBar, they are the private variables you now can use in your code.
pcFoo and pcBar are not declared if the code that ran before your code did not create them. You're stating that you want to make use of them, if they exist, you may also state that you actually expect them to be created and need them and your code only works on the premise they exist. You can easily check by using TYPE or VARTYPE that its the case and act alternatively, if not. ASSERETS would also make this clear in terms of code and not just documentation. But it gives you a chance to actually document the use of private vars used in your code and their name. Quite like ETERNAL ARRAY does specify a specific name you use in your code accessing array elements of that name, that may (or may not) be defined by earlier running code.
And here understanding what PRIVATE vars mean and how they can help and be used comes into play. If you have created a private variable called pcFoo and allow it in by using PRIVATE ALL EXCEPT pcFoo, then this does not need to be passed in, you save the time for variable declaration and setting its value, it's already there and you just continue to use it.
Also let me come back to what is meant with "call stack" and "local vars of other code that's still on the call stack". You can decide for private vars as a scope somewhere between LOCAL and PUBLIC, but what are the actual boundaries of the scope? It's a strange scope, and an ugly one, that's why no other language has it. It's something like a compromise between a property of a class that's available not just locally in a class method and not publicly anywhere but only within class code and subclass code, depending on the actual visibility modifier that can be hidden, protected and also private.
And - well, this isn't only my interpretation but it surely is an interpretation, I guess the class inheritance visibility types are the origin of what they are called PRIVATE vars. It's just not limited to a class, the scope is about anything that is called, whether it's a method of the same object, a function within the same PRG, or whether you call a PRG that exists independently from the class definition or a function within a PRG file - anything. The topic is just being called, and that relates to the call stack.
The call stack is a natural organisational data structure any programming language has, the call stack starts empty and the first thing that goes on it is main.prg and all the variables of main. When you call something, that actually saves the current environment including local vars into a memory and puts the called code on the stack. You can see this visualized in the debugger call stack window, that represents the temporarily stored things as the name of the code that previously ran. PRIVATE vars are not stored away or hidden in that step of making a call and preserving the local environment to be able to get back to it. What the call stack remembers includes the position in the code, where to return to. And that can also be seen in the Trace window when you click on some previous level line of the call stack.
In this sense the LOCAL scope is just a side effect of storing away local vars on the call stack memory. It also is related to the hardware feature of CPU call stacks, which save CPU register states. It's in short a mechanism that allows this kind of code calling and concurrency without actually needing a core to be kept in a state to be able to return to. That's also a reason calling something or returning back has a time cost on its own, to restore the state, the locals and the current position in code. A time cost outside of the need to pass on parameter values or returning a result. Well, and in how this works in detail isn't documented. If you think a large array is actually stored there then, no, actually just the state of VFFPs Name table index, the internal VFP structure to know about the currently available variables and also objects, and their properties and tables and their fields.
As you know tables and fields stay available and only become "hidden" when you change datasession by switching the code context with a call or a return. That's separate from the call stack, yet I guess also related, as it must be clear to VFP what data session code runs in and that has to be made active.
Overwhelming again?
Let's summarize The points I want to make:
You have a chance to control the private variable scopes and their flow by actively controlling the scope with PRIVATE ALL EXCEPT statements at the start of any code, you then can make good and documented see of them.
PARAMETERS is part of the puzzle as it allows starting new private vars. It's not, as it might sound enabling to use private scope as vehicle of passing in values. Actually using privates means NOT passing them in, but just continue using them as they already exist. In that sense, the private scope can accelerate things as private vars don't need passing in, neither by value, the time expensive mechanism, unless the value itself is just as short as the memory address you need to pass over for the "by reference" passing mechanism.
You don't need privates, the actual best experience about side effect free code is by tightening scopes as much as you can and in that sense LOCAL is VFPs best scope, we don't have block scope, as some other languages have, that's even tighter/shorter.
You always have workarounds, even if you decide for now only using FUNCTIONS and their bracketed parameters to save a line of code. I know some would argue to better use a PRG anyway, but it's a usual way to define methods of classes, when you define the classes in PRSs. If you want these parameters to become privately scoped, you can always copy the local parameter variables into privates. You can also decide against the use of private scope completely, defend yourself against other peoples' and your own errors of accidentally not declaring something LOCAL by using PRIVATE ALL only and everywhere. Just don't make that a habit to use privates as locals then, you'd depend on everybody using PRIVATE ALL and you can't force it on existing code and other libraries/frameworks.
There's nothing that makes you depend on that scope. I showed earlier REPORTS actually can access LOCALs that are scoped to the code that makes the DO REPORT call. Weirdly access to locals of your code is not only available within report controls - also any report method can see them, they are "magically" not hidden away as they normally would. VFP acts as if the whole report code is running in the one REPORT FORM line of code.
I already also discussedthe possible use of privates for SQL parameterization. And there is another case, where you might want to enable an alternative to passing values by parameters: When you do a FORM, you can use parameters, but they arrive late, in the form INIT. Usually, the INIT is the first thing running in an object, but in forms before INIT the LOAD event runs and the form data environment runs and both of these could make use of values of private vars.
And that actually was a mechanism very frequently used in legacy VFP screens programs. Where the whole screen actually often is a single thread of code running to the end of the screen code with read states, so private vars are then available to any code within a screen and become like a private property of the screen. I think that once was their major usage.
Now, if you start a non-modal form, you can see private vars in load, data environment and still in init, but once that's done and code continues after the DO FORM line of code, your form stays in scope, perhaps, but any form methods or events don't see the private variable anymore, it's released after init returned to the DO FORM statement and code continued.
Well, and that's also a good way to end this, at the end of code, when it returns to the caller, not only locals, also privtes that were created on that same stack level are automatically released at that point, that ends their scope.
Chriss