Scope of $variable in Let statement in Calculated Field

You could try naming the custom function's variable differently. Preferably drop the '$' identifier. If this yields a different result, this hints at a scope issue.

2 Likes

Further testing suggests it's a very simple issue - in a script, if something (such as a Set Field step) causes another calculated field to update, any $variables used in the calculated field's calculation will be in the same scope as $variables used in the script. It doesn't require repeating fields or JSON or any thing fancy.

  • if you use regular variables (such as 'N' instead of $N) in the calculation field, it doesn't happen
  • if the calcuation field is not stored, it doesn't happen (presumably because it doesn't update when you do the Set Field step?)
  • this behavior happens both in FM18 and 19, so it doesn't appear to be new.

This seems like a very subtle and devious trap, because:

  • when editing scripts, the Set Variable script step automatically adds $ to variable names -- if you enter 'N' you get '$N'
  • because of this, it's natural to assume that $N variables are the same as non variables without the dollarsign, and both are local to the enclosing context
  • if you then copy a calculation from your script to a calculated field, it's easy to just copy & paste, and then you are using $ variables in the field calculation
  • becuase of how it works in scripts, I naturally assumed "$" variables are local to the field calculation. This tuns out to be wrong.

You should note that $var variables are scoped to scripts. You can define a $var variable in a let statement in a field calculation, however when a script runs with the same $var variable and the table is in context, both are one and the same.

4 Likes

The set variable step always adds a $ character to the variable name. That's the recommended scope. You are able to add a second $ character to create a global variable. You cannot remove all the $ characters. When you do so, a $ character will be added to the variable name. This makes sense. A variable without a $ prefix can be created within calculations that use the LET and WHILE functions. Any variable that you set with the Set Variable script step must be local or global to survive beyond it's own definition and be used in the script.

Having said all of that, I've just experimented and found exactly the problem that you are describing. This seems like a nasty bug to me. Will you report it?

My test is attached

VarLeaks.fmp12 (292 KB)

@Malcolm - thanks!

Here's my project file:

variable_scope.fmp12 (368 KB)

I'm still unsure about this - the behavior is really unexpected to me, but maybe everyone knows that's how it is supposed to work? I appreciate others saying they find it weird too...

This is a bug that could have serious consequences. No-one wants to have values from field calculations bubbling up into their scripts.

It is an excellent reason to ensure that you limit the scope of your variables as tightly as possible. In your case, the variable $N can be more tightly scoped by removing the $ character, making it a calculation variable.

1 Like

reminds me of this thread:

3 Likes

FMScript handles scope not the same way other known languages like C, C++, C#, Swift, Java etc. do.
The scope of a $-scoped variable is the executed script, that of a $$-scoped variable the executed file and variables declared in functions that have no $ or $$ scope designator have the function as scope. In the above-mentioned languages, functions do not know variables that have been declared in another function. The compiler guarantees that scope is respected and there is no interference between functions.
FMScript does not enforce variable declaration and lets live $-scoped variables in functions. The responsibility for controlling scope between script and function lies entirely with the developer, the script engine does not throw a warning.
Declaration of variables and strong scope are missing in FM since many years.
Avoiding the use of script-scoped $variables in functions is a good workaround.

2 Likes

This can also happen when using the Data Viewer: Overriding Variables using the Data Viewer

And applies to Custom Functions as well.

hey @MonkeybreadSoftware - would it be possible to create a feature to audit our calc fields and scripts?

Warning: Table::FIeld is a calculated field which uses $variable names, which could have unexpected side effects. Consider rewriting the calcuation using plain variables instead.

if you see a custom function with a local variable assigned or embedded in a statement / expression within then run as fast as you can!

3 Likes

Or wrestle the beast :t_rex:

1 Like

All this discussion of scope is perhaps confusing people. Here's a very simple way to describe what actually happens:
When a $variable is defined, it is shared across EVERYTHING while it is defined.
When a SCRIPT defines it, it goes away when that script finishes running.
When something else defines it (Field calc, layout conditional formatting, button-bar label calc, etc, etc.), it is defined in what I've seen people call "script-zero" scope: In other words, EVERYTHING sees it, and it sticks around while that object-with-a-calculation is evaluating it.

So, moral of the story: If you do not NEED (and know why you need) a $variable in a Let or While, do not include the $ symbol - use truly calculation-local variable names.
If you DO use $variables outside of an actual script, use them VERY carefully. Give them VERY UNIQUE names.

I've seen custom functions, which could be run in all kinds of contexts outside of scripts, use names like $num to do some kind of across-recursion-level processing of info. If you must use a custom function like that, rename its variables to $_NAME_OF_CUSTOM_FUNCTION__whatever_they_named_it. Better is to avoid those custom functions, if you can.

6 Likes

Thank you @DanShockley

Also, I wonder about this:

To clarify, I almost never use $$ variables, but I assume these act like global fields, e.g. global to the file, but global only on a per-session basis (e.g. User A's $$foobar variable will not affect user B's $$foobar varaible). I would assume this keeps $$variables isolated across Scripts that are running in PSOS mode?

Correct. $$variables are unique to each user.

What about if the same user is connected to the database twice? :slight_smile:

:thinking: Interesting

I assume user sessions are independent of each other. I know people using iPad and desktop at the same time.

1 Like

$$variables are unique to a file, from the moment it is opened to the moment it is closed in a client. Client, here, means FileMaker Pro, FileMaker Go, FileMaker WebDirect or a server-side session invoked via PSOS or a scheduled server script.

2 Likes

Right. And, @xochi - global variables being per-session, per-file also applies to the same account logged in using some other client: they do not share across those clients, even using the same account.

1 Like

What you describe is not a bug, it is known, expected behavior. Though I will say that (like many things), it is not documented very clearly, and that causes a lot of confusion.

@DanShockley's description of variable scopes are excellent. I haven't heard the "script-zero" reference before, but the description of "it sticks around while whatever defined it exists or is being evaluated" is how I think of it. And calculations can be anywhere… field definitions, Data Viewer, countless places on layout objects, etc.

If you want your field or layout $variables to "interact with" script variables, this can be a powerful feature. But if you weren't expecting it, it will seem like a bug and will drive you batty.

Unless I really want this, I stay away from using $ or $$ anywhere but a script. Inside Let statements, I want to use something to help me see visually that it is a variable, and also avoid reserved words or other function names. I've used ~VariableName because it stands out and is easy to type. A colleague of mine also got me in the habit of using ¢VariableName. It was a clever way of denoting the desired scope significance. $$ for global, $ for script, ¢ for calculation. I suppose if you live in the EU, you could use € or £, as needed. :wink:

I would also caution that any variable, global or otherwise, is at most scoped to the current session, and avoid saying that they are scoped to the user, computer, etc. A user can have multiple concurrent sessions, in several ways: multiple computers/devices, multiple WebDirect browsers/windows/tabs, multiple instances of FileMaker running at the same time on the same computer, etc. This will all have their own session, with their own global scope. And variables do not transfer to PSOS sessions, so each of those need to re-establish context and variables, if needed.

5 Likes

A Tilde (~) for LET statement scoped variables is the norm that I have seen, but I like the ¢ / $ / $$ paradigm for scope using the monetary unit increments.