Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

LPARAMETERS and PARAMETERS statements 4

Status
Not open for further replies.

AndrewMozley

Programmer
Oct 15, 2005
621
GB
I have never been sure of the difference between LPARAMETERS and PARAMETERS.

Have written a short test program to see if I can detect the difference.

Code:
CLEAR
SET TALK OFf
LOCAL X
x = 2
SET STEP ON
lAns = MyFUNC(x)
? "lAns = " + STR(lAns,2)
? "vParm = " + STR(vParm,2)
RETURN

FUNCTION MyFunc
PARAMETERS vParm
*  or LPARAMETERS vParm

   vParm = 3
   RETURN vParm

In each case the results are the same; (the program also gives an error on the last ‘? “vParm = “ . . . ‘ statement)

Would be grateful if anyone cares to provide code which shows the difference between LPARAMETERS and PARAMETERS. I have never learned!

Thanks - Andrew
 
Well, you could starat searching PARAMETERS( including the starting bracket to ensure you don't use that function or replace it with PCOUNT(, after that replace PARAMETERS.
You also have search with regular expressions, which enables nailing it to PARAMETERS at the start of the line. And can't you search for PARAMETERS including a trailing space?

I think there are many possibilities to do it right and automatically without manually confirming each replacement. Be cautious, right, but be clever to not need manual work here.



Chriss
 
Thanks again for the explanations. I had not been accustomed to using the LPARAMETERS statement - felt I need to learn about it, and you have all been helpful. But will probably leave existing code as it is. So, when defining a function, using :

Code:
FUNCTION MYFUNC (<Param1>, <Param2>)

I understand that this works in the same way as the LPARAMETERS construct, giving LOCAL variables. - Andrew
 
Yes, I already said so.

You find it officially in a note in the help topic about the FUNCTION command:

Help said:
Including the parameters inside parentheses (()) immediately following the function name indicates that the parameters are [highlight #FCE94F]locally[/highlight] scoped to the function.

also in the description of the bracketed parameters:
help said:
Assigns data from the calling program to [highlight #FCE94F]local[/highlight] variables or arrays. You can use the AS para1type clause to specify the data type of the variable.

I guess that's about triple insurance, you can also always use LIST MEMORY to see what happens at runtime. Which also shows you both hidden and currently visible private vars, if you want to check that out. Aside of checking that with the debugger watch and locals windows, to ensure yourself about all that.

In older Foxpro versions only having PARAMETERS, you also can't have parameters defined in brackets. That's the reason you only have private parameters in legacy FoxPro.

Chriss
 
Another consideration for LOCAL vs PRIVATE: Any variable(s) created with SCATTER MEMVAR which were previously assigned as PRIVATE could cause a messy situation.

To prevent this you could assign LOCAL to the field names before issuing SCATTER MEMVAR. It may require a loop if there are a large number of fields.

I wish LOCAL were the default instead of PRIVATE. Oh well...

Steve
 
Correct Steve, but I guess that's the history of how the language evolved.

The usual name clash problem comes up if you forget to declare variables local at two places in the code, at a single place this only hurts, if an assignment to a variable that should have been local overrides the value of a private var, that likely also should have been local. And that would not happen if local was the default scope.

Doing everything with local vars and parameters saves you from both harming callers bleeding in private vars and being harmed by called code that doesn't pay attention to variable declarations. And these errors happen because a LOCAL declaration can easily have a typo in the declared names. If used names in the following code differ, you still get private vars instead of errors. That's the root of problems.

Chriss
 
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
Code:
PRIVATE ALL

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
 
Hi Chris,

Wow! Great explanation. To be honest (and perhaps ignorant), as I read it I thought to myself I could do all (or most) of it with my heavy use of form properties instead of variables which I may want to behave in certain ways. I don't have be concerned that I might have used a new variable name in some higher-up method, function etc. Anyway, I'm too old to change my ways and it has stood me well so far.

Steve [bigglasses]

p.s. Wouldn't LOCAL ALL be nice??
 
Hi Steve,

thanks. In a way, if I'd be pedantic LOCAL ALL is the default, as all locals are hidden from you, but I know what you mean, actually changing the default scope of variables you declare by assignment to be LOCAL.

What's actually bad is the compiler allowing you to remove a LOCAL statement, if code actually first sets variables they now get another scope. Besides that other languages variable definitions and declarations are not about their scope at all but about their type. And that's missing in VFP as dynamically typed, while it still plays a role in strongly typed table fields, of course, and functions needing specific parameter types. Why that strictness on one side and lose definition on the other side?

I don't think it's bad to need to declare variables LOCAL, at these lines you can add a comment describing what they mean. Okay, you can make that same comment in the assignment that at the same time declares them. The major point may be, that you can't forget the usage of a variable, so making it its declaration at the same time means you can't do the error of forgetting the declaration. Remove the need to do unnecessary stuff. You're lucky VFP isn't like C/C++ making a difference of the definition and declaration of a function. In the basic case they seem just redundant.

Chriss
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top