Variables & Scope Revisited for FileMaker 18

Back in 2014, I wrote a blog post exploring the different types of variables in FileMaker and their scopes:

  1. Global ($$myVar – at the file level for the duration of the user’s session )
  2. Local ($myVar – at the script level for the user’s execution of the script)
  3. Calculation( _myVar – at the calculation level, for as long as it takes to complete the calculation. Unlike Global and Local variables, they don’t have a mandatory prefix. I tend to use an underscore to make sure they do not get confused with other function names or field names.

An important fact is that lower-level scopes have access to variables declared in higher-level scopes. This is why all scripts and calculations have access to $$ variables, and all calculations in a script have access to the script’s $ variables.

At the time I wrote the original blog post, the Let() function was pretty much the only place where you could create a new variable inside a calculation (any of the three types) which is why I named that scope “calculation”.

It’s time to refine that 3rd level a bit and call it “Function variables”. The reason for this follow-up is the new While() function that is introduced in FileMaker 18.

While() Function

The anatomy of a While() function call breaks down into four parts:

Screenshot of the initialization or setup part of the While() fuction call
Part 1: Initialization
Screenshot of the condition part of the new While() function call
Part 2: Condition
Screenshot of the logic part of the new While() function call
Part 3: Logic
Screenshot of the outpart part of the new While() function call
Part 4: Output

When we look at the initialization part of the While() function, we’re actually declaring variables: “i” and “out”. Similar to what we can do in a Let() function call:

Let(

[
i = 0 ;
Out = “something”
];

Out & “-“ & i
)

Given that we now have two functions, Let() and While() that can create variables, what happens when we use both functions in one calculation? What happens to a variable that is created in one function call and used in another function call within the same calculation?

The help for the While() function spends some considerable time on this, which underlines the importance of properly understanding its implications.

Let’s start with this modified version of the example in the online help.
This While() is perfectly constructed, all of the variables it needs are declared, the condition is clear (it will run 3 times), and the logic will increase both the _total and the _count variables and output the _total variable when done. I’m using my preferred prefix (“_”) for the function variables to make sure there is no confusion about what they are.

While ( 
	[ 
		_count = 1 ; 
		_total = 0 ; 
		$a[1] = 25 ; 
		$a[2] = 50 ; 
		$a[3] = 75 
	] ; 
	_count <= 3 ; 
	[
		_total = _total + $a[_count] ;
		_count = _count + 1
	] ;
	_total
)

The fact that we also declare a script-scoped variable $a inside the While() has one big consequence: _count and _total will be wiped out when the While() function ends, but the $a variable with its three repeats will maintain their value.

The nested Let()/While() calculation below will produce the same result:

Let(

[
	$a[1] = 25 ; 
	$a[2] = 50 ; 
	$a[3] = 75 
];

        While ( 
		[ 
			_count = 1 ; 
			_total = 0
		] ; 
		_count <= 3 ; 
		[
			_total = _total + $a[_count] ;
			_count = _count + 1
		] ;
		_total
         )
)

Why does it produce the same result? Because the Let() function now declares the script-scoped $a, and that script scope supersedes the function scope while() has automatic access to those variables.

What happens when we move one of the While() function variables to the Let()? See the red highlighted change:

Let(

[
	$a[1] = 25 ; 
	$a[2] = 50 ; 
	$a[3] = 75 ;

	_count = 1
];

While ( 
	[ 
		_total = 0
	] ; 
	_count <= 3 ; 
	[
		_total = _total + $a[_count] ;
		_count = _count + 1
	] ;
	_total
)
)

We now declare the _count variable in the Let() and use it in the While(). The result, however, may be surprising; when we execute this calculation the result will be “?”, indicating that the recursion limit was exceeded. In other words, the While() function did not execute the logic block three times, it looped 50,000 times and still could not satisfy the exit condition (“_count <= 3”) despite our logic block clearly incrementing our _count variable on each iteration.

The crux here is a very particular behavior of the While() function: the variables you manipulate inside the logic block will only maintain their value if they are declared in the initialization block. What this means is that _count will be incremented in the logic block, but on the next iteration, it starts with its initial value, not the previously incremented value. It will never increment to three.

That may lead us to think that the While() function has no access to the variables from the Let() function. But it does.

Consider this example:

Screenshot of Let() function
  • On line 8 we are declaring a _name variable, in the Let() scope.</li<
  • On line 18, inside the While() scope we are modifying that _name variable by appending an exclamation mark. Note that the _name variable is not defined in the While() scope.
  • On line 22 we output a string from the While() function that includes the value of the _name function.
  • And finally on line 24 the Let() function appends its own string to the outcome of the While() function: it adds the value of the _name variable as the Let() function knows it.

The result is this:

Screenshot of result when the Let() function appends its own string to the outcome of the While() function

The result shows that the While() did properly execute 3 times, as we expected because it is properly constructed with the _count variable declared in its initialization block.

But clearly, the While() function has access to the Let()’s _name variable because it modified it by adding the “!”. We only get one “!” appended though, not three as we might expect. That is because of the behavior we outlined earlier; the modified value of _name does not get retained with each iteration, so each iteration starts with “wim” and adds an “!”. When the While() function ends, it has “wim!” as the final value and uses that in its output.

But when the While() ends and control is passed back to the Let() function, While()’s copy of the _name variable is terminated. And Let() does not know (or care) that While() modified it. As far as Let() is concerned the value is still “wim”.

In conclusion, it is safe to say that scope just got a little more complex and that there are three main things to remember:

  1. A nested function has access to the function variables declared in its ‘parent’ or ‘outer’ scope.
  2. A parent function has no access to the modified variable from a nested function.
  3. If you need to modify a variable inside a While() with each iteration of the loop, you have to declare it in its initialization block. You cannot use an inherited variable.

As always: feel free to leave questions and comments here on the blog post or find me on community.filemaker.com.

5 thoughts on “Variables & Scope Revisited for FileMaker 18”

  1. Glad to see while introduced to Filemaker! Interesting that you can now do multiple lines in 1 single function.

  2. One FM consultant told me that Custom Functions run in their own memory space and for that reason are faster than the WHILE function. That sounded intriguing. I personally hate Custom Functions and only use them as a last resort and have been looking forward to replacing many of the recursive custom functions with WHILE. But may have to think about this depending on the performance hit. Do you know about this or have any performance comparisons?

    1. I have not done enough side-by-side comparisons to determine whether the same iteration in a Custom Function would be consistently faster than in a While() outside of a Custom Function. As noted in the FM18 Executive Summary though; here should be no expectation about speed advantages over other methods (recursive CFs, looping in a script,…) especially for very large number of iterations.

      As to Custom Functions in general: we love them and we use them liberally for a wide variety of functionality.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top