And just to be clear: they should most definitely not be. But it is tempting to make them so. What is not always clearly understood is why it is not a good idea to declare variables “out of scope”.
It’s all about Scope, Lifetime and Visibility of variables. Those topics are not unique to FM, they apply to all coding environments and the same best practices apply to all of them.
The directive is that you only declare the variable for the scope that it needs to be used in. FM has 3 broad scopes:
- the file
- a script
- a calculation (this is murky in FM because there are so many places where calculations can be specified: a field def, script steps, layout elements like visibility, conditional formats, web viewers, charts, etc…). Some calculations are explicitly controlled by us (a Set Field script step for instance), but some calculations will fire outside of our control (for instance when one needs to display the result of an unstored calc).
FileMaker has three types of variables for use in those different scopes:
- global variables: they have a double dollar sign prefix ($$myvar) and once declared they will retain their value until the FileMaker file closes (or some scripted or calculated action clears their value). You’d typically use the “Set Variable” script step to declare them.
- local variables: they have a singe dollar sign prefix ($myvar), these variables are typically declared in a script and retain their value until the end of the script (or until a scripted or calculated action clears their value). You’d typically use the “Set Variable” script step to declare them.
- calculation variables: there is no preset prefix or naming convention and I typically use an underscore for them (_myvar), these varaibles are declared by a Let() function call and only retain their value until the calculation is calculated. The Let() function can also be used to declare local and global variables.
Ideally you want your variables to have an automatic lifetime in the sense that when the scope closes the variables expire. The variables are cleared automatically by the program. FM does that for all 3 scopes: $$vars get cleared when you close a file, $vars get cleared when the script ends and your Let() variables get cleared when the calculation ends.
Where possible you want the program to clean up after itself and avoid having to do it yourself in your code. Taking advantage of the automatic expiration of variables significantly reduces the risk of errors and confusion. It should also – at least theoretically – reduce the memory resources that your solution needs.
This is typically where you run into problems by not limiting variables to their scope and not taking advantage of the automatic lifetime management. The value of a $$var is available in every script, in every calculation. The value of a $var can be used throughout the duration of any script (even when you declare it part of a calculation outside of a script!). When you declare a $$variable in a script so that another script can also access it, it will still hold its value when your scripted flow is done and you did not explicitly clear out the variable.
That “visible when it should not be” can clearly cause confusion but it can also be a security risk. Variable-leftovers can be visible in the data viewer when you don’t want them to.
If I declare a local variable $otherVar or a global variable $$var a in a Let() function in a calculation for instance, like this:
let( [ $$var = "something" ; $var = "something else" ; _var = "lorem ispum" ]; $$var & " - " & $var & " - " & _var )
Then both $$var and $otherVar will still be visible when the calculation ends. $$var will be around until I close the file. “_var” however will just last for the duration of the calculation and will not be available past that point.
If I really only needed $$var or $var to get to the calculation’s end result, then I have to remember to clear them out, or risk accidental use of those variables values. In most cases it is better to not declare $ and $$ variables in the Let() function in the first place, use another notation like _var and _otherVar (there are no strict rules about naming calculation variables).
Say that in my script I had already declared $var and set it to “my value”. Later in the script I do (or FM does) something that fires the calculation above. Then $var will contain “something else” and not “my value”. That may be your intention but it may not be. Since there are so many places where we can use calculations, using them to declare local and global variables does carry the risk of changing values of the same variables declared elsewhere.
The thing to remember is that when you use variables outside of their scope then the burden is on you to make sure they get cleared when they are not needed. If you do not, then it becomes really hard to troubleshoot and figure out when variables have a meaningful value or when their data is stale and meaningless.
So when do you use what type of variable? If you are in a script and you need to pass on some piece of data to another script, the ideal thing to do is to pass that piece or pieces of data on to the next script as a script parameter where they can be parsed as local variables for use in that script. This way, both scripts will clean up after themselves. When you need to revisit the scripted flow a few months later you do not have to worry about what happens to the variables in scripts that either call or get called from your script.
It is easy to side-step some of this extra work and use global variables instead. I am certainly not saying to not use global variables ever. But use them only where you need to and nowhere else. For layout merge variables for instance: absolutely, that’s the way to go. But for making info available between scripts: not so much. The preferred approach is to pass parameters to the other script.
Passing one or more multiple parameters between scripts and especially returning a result from a script to be used in a a calling script is easy to implement. A number of techniques have been developed over the years and take all the work out of it. One approach can be found at ModularFileMaker.org.
If you use local variables and use some of the available techniques for passing parameters to the subscripts you can be assured that all variables will get cleared when the process is done. Automatically. Easy.