BlogFileMaker

Variables & Scope Revisited for FileMaker 18

By June 6, 2019 5 Comments

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.

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

  1. Initialization or setup
  2. Condition that determines how many times the While() function will loop (While the condition is true, execute the logic)
  3. Logic; one or more calculation statements that will be executed in every iteration
  4. The output for the function call
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.

Wim Decorte

Wim Decorte

Wim is a Senior Technical Solution Architect at Soliant. He is a FileMaker 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 and 17 Certified FileMaker Developer and the author of numerous Tech Briefs and articles on FileMaker Server. Wim is one of the very few multiple FileMaker Excellence Award winners and was most recently awarded the FileMaker Community Leader of the Year award at the 2015 FileMaker Developer Conference. He is also a frequent speaker at the FileMaker Developer Conference and at FileMaker Developer groups throughout the world. In addition to being a renowned expert on FileMaker Server, Wim also specializes in integrating FileMaker with other applications and systems. His pet project is the open source fmDotNet connector class that he created.

5 Comments

  • Avatar David Tinoco says:

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

  • Thanks, Wim for documenting relationship between While & Let. And I like “Function variables” name.

  • Avatar Daniel Wood says:

    Cheers Wim great article very easy to follow and covers the little gotchas well.

  • 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?

    • Wim Decorte Wim Decorte says:

      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 Reply