Is this possible

I found this in the field definitions of a database I'm probably taking over.
Is it possible in any way to use non-global variables in a calculated field like this? Any plug-in who can do this? It does not work but why has it been made?

Let (
trigger = productcode
;
Case (
$new and IsEmpty ( Articles::ID ) ; discount
; $new ; Article.ArticleBase::discount
; $script or $import ; Article.ArticleGroup::discount
; $dup ; discount
; discount
)
)

Well, I see a few things wrong:

  • What is productcode ? It's not a global of local variable
  • discount is specified without a table name twice so it shouldn't work

Do you know which value should this field should hold ? This is a weird calculation. Is that field set as a calculation field or text field ?

1 Like

A couple of things could be happening.

  • The variables are set to be set during a script run. And setting the field referenced in the trigger variable will trigger the calculation to fire.
  • The variables could also be set in a Custom Function, or some other calculation the fires outside of a script.

You should be able to run a DDR and search for references to those variables to see where they are set, and what actions or script set that trigger field.

1 Like

Hello @LucThomaere

Perhaps reading this blog post will help answer some of the questions you may have related to the calculation and the presence of $variables in it. Usage of such $variables is allowed, but understanding their behavior requires a clear understanding of scope -- something which is explained very nicely in the blog post cited above.

If I had to guess, I might venture that this is the field definition for a "discount" field in a table of something perhaps similar to line items. The use of the $variables allows the calculation to branch and conditionally populate the discount value with various alternatives for its value, depending on the values populated in the $variables, at the time that the productcode field is updated. I imagine that to use this properly, one might be expected to populate the $vars appropriately before certain actions, e.g. set $dup to True before duplicating the record, or set $import to True before importing records into the table. This is all just a guess on my part. I hope it may help.

IMO, one of the downsides of this type of calculation is that its workings may be readily apparent to someone who is well-versed in the style or the solution, but otherwise, it can be a little mysterious to someone who is new to the solution, and, for instance, it may not be obvious to you that certain $vars must be properly populated before performing an action such as duplication of a record, or importing records.

Kind regards, and hope this may help.

-Steve

1 Like

I'm going to assume that productcode and discount are both fields that are in the same table context so they don't need a table name reference. The purpose is obviously to check the environment and determine what discount to apply.

The mystery is, where are the variables being populated?

One possibility is that productcode is a custom function and that is being used to generate the local variables. It's possible to pass local variables back into a script using CF so I assume ( I have not tried it ) that it would work in a field too.

Can a calculated field use the value of a local variable ($) that is set during a script run? I thought that was only possible with global variables (preceded by $$)
I will investigate that...

Yes, it does work. I learned something I didn't know for so many years.
The auto-enter calculated field can use the value of local variables during a script run. That's fine.

But of course the script itself could also change the field directly instead of using a auto-enter calc option. So at this moment I don't see the advantage of this technique.
Thank you all for the info.

I've just tested it too. Interesting to see how broad the scope of a local variable can be.

I use local variables in layout calculations to generate data for merge fields, and other uses. They persist until the layout is redrawn, so they are very dynamic and responsive.

If this behaviour is not documented, isn't it dangerous to use it because the next version the behaviour could be different or stop to exist at all?

In general, yes.
In this case, it's fairly fundamental behavior so the chance of it changing is slim; but it is very real.

I wouldn't do it.

Only if the calculated field refreshes at the moment that the script is still running. Which is very unpredictable and I wouldn't count on it always working when you want it to.

There is the concept of "Script 0". For instance you can use a Let() to declare a variable in the data viewer. There'll be no script running so what is the scope of that then? That's script 0. As long as your Data Viewer keeps the $ alive it's value will be used everywhere that the variable is called and not explicitly just overwritten in the context of another scope.

Same with $ that you declare as part of say an unstored calc. Their scope will be that invisible script 0 and it may totally throw you off.

So don't declare or use $ variables except explicitly in an actual script. Doing anything else will yield unpredictable results sooner or later.

1 Like

Thank you, Wim,. I already have a natural aversion of using that kind of "tricks". Lots of things last longer if you keep it simple.

I was caught by this behaviour and I believe it warrants some warning.

Variables you define in the data viewer are available to the files in use. This allows us to do debugging and ad-hoc data injection into scripts. It can also interfere with the operation of the files in use.

Do not leave code snippets that define variables in the data viewer when you are not working with them. They can cause problems.

This behaviour is documented. See FileMaker Pro Help (inserting merge variables)

The documentation describes how to create and use merge variables. It is careful not to specify local or global. The documentation doesn't ever say, "do not use local variables". However, at one point it says: "It’s a good idea to create global variables for use as merge variables." So there is a recommendation but there are no rules and no prohibitions.

The problems with using merge variables on layouts become apparent when you use them in a list context, either list view or in a portal. If you have defined a variable using a calculation attached to a layout object and the calculation draws data from a field, then you expect to see different results on each record. On form layouts this works perfectly. On list views the rendering engine struggles to handle this.

This issue affects global variables just as much as it affects local variables. The recommendation to use globals is a strong hint. The default purpose of a global variable is to share a single value across records and across tables. If you expect your merge variables to behave in that way then they will work exactly as you expect.

In the screen shot below you can see that the use of local variables is working properly when the window is in form mode and in list mode. When the window is in list mode and the record count increases this method does not work reliably. You can also expect problems in multi-window situations as the layout objects are not updated in the same way as fields. There is potential for values displayed in merge objects to lag behind the current data.

1 Like

There are valid use cases for script variables in field calculations (not calculation fields). Auto-entries based on run-time conditions is an example: a script runs; then pauses; a user creates or edits a record, causing auto-entry fields to update; the script continues, then exits, clearing the runtime variables in the process.

IMO, the chief problem with using script variables in field calculations is one of documentation. Same goes for global variables. It is a nightmare to maintain code if poorly or not done. Poor error handling is another pain point.

2 Likes

Those two go hand in hand... mind you: my reaction was to instantiating variables out of scope, as to calling for a variable's content: it makes sense to use them wherever you need to. It is not the act of using the value of a variable that causes trouble, it is declaring a variable where its scope isn't the normal scope that may cause interference.

Just like with any other environment that has scope: nobody is ever going to mandate that it is forbidden. There is always going to be that one use case where you have no other choice. Like @Malcolm's layout use. I use global variables there too to measure the loading speed of a layout for instance. Proper namespacing of those variables avoids collisions with anything that happens elsewhere.

2 Likes

:+1:t2: 1,000,000

Not handling errors is you worst enemy that can hit you once your app is in production. One needs to anticipate what can go wrong and handle these conditions right at the beginning.