Jump to content

How can I use the let function to set a $localVariable?


This topic is 3356 days old. Please don't post here. Open a new topic instead.

Recommended Posts

Newbie Let function question.....

 

I am using the Let function for the first time. My goal is to end up with a local variable "$DaysOld" (which equals the "current date" minus "date of birth").

 

My Let function below is returning the answer that I want but how do I assign that answer to a local variable?

If (
	METADATA::desc ≠ "birthday" ;
	"" ;
	Let ( [
		~dob = GetAsDate ( METADATA::value ) ;
		~curdate = Get (CurrentDate)
		] ;
		~curdate - ~dob 
	)
)
Link to comment
Share on other sites

You can create it as a side effect:

Case (
  METADATA::desc = "birthday" ;
  Let ( [
    ~dob = GetAsDate ( METADATA::value ) ;
    ~curdate = Get (CurrentDate) ;
    $DaysOld = ~curdate - ~dob
    ] ;
    ""
  )
)

But why do you need a $var in what I assume is a field calculation? (In a script, you could of course use Set Variable [ $DaysOld ; … ] )

  • Like 2
Link to comment
Share on other sites

I don't understand your question Bruce. I am not using it in a script. I am using it on a layout to display data...so I do not have to create calculation fields solely for presentation purposes.

Link to comment
Share on other sites

Layout variables must be global variables ($$).

 

Normal practice is to run a script to set that variable.  If you try to bury setting that variable in a calculation (like a conditional format calc) on a layout then:

- you are hiding business logic: makes it hard to troubleshoot and maintain.  Your future self will hate you for it.  So will any other developer that is tasked to work on the solution

- if you ever need the same logic on another layout you have to code the same calc again.  If the logic ever changes, you now have multiple places to update the logic.  Not good.

Link to comment
Share on other sites

It is not good idea to use $variables within Let().  A perfect explanation exists on Tech Net here https://fmdev.filemaker.com/message/105194#105194 but since many people aren't registered at Tech Net, I'll quote the explanation here (by Comment):

 

 

The Let() function purposefully creates a "sandbox" for its own (unprefixed) variables; their scope can never exceed the limits of the parentheses. For example:

 

Let ( [

a = 1 ;

b = Let ( a = 2 ; a )

] ;

a

)

 

returns 1, even though the variable's name has been reused within the same calculation. Now, when you use $variables, you extend their scope to the entire file (with the exception of scripts). One effect of this is that when a calculation such as:

 

Let (

$futureDate = Get (CurrentDate) + Numberfield

] ;

$futureDate

)

 

is evaluated, the value assigned to $futureDate becomes valid for all the records in the file. The only thing that keeps all records displaying the same result is Filemaker's laziness in refreshing the display. That's something I am not willing to trust, even if I don't have a practical example of a failure (so far?). I just tell my variables to keep their heads and hands inside the bus...

 

 

 

 

Layout variables must be global variables ($$).

 

 

Local $ variables used to work as layout (merge) variables also (in version 11) but more recent changes in layout rendering and refreshing has removed that ability.

Link to comment
Share on other sites

Layout variables must be global variables ($$).

Your future self will hate you for it.  So will any other developer that is tasked to work on the solution

 

Yes, but think about the added benefit of job security. :)

Link to comment
Share on other sites

I believe this ( $local variables ) still actually shows as a merge variable depending on how/when you set it, though it works a little different than before. But I agree that you really have to be careful with there use. Most of the time I use them, it's strictly for UI elements. As Wim's article nicely points out, use the variable they way they were intended. And save yourself some headaches. lol

 

Local $ variables used to work as layout (merge) variables also (in version 11) but more recent changes in layout rendering and refreshing has removed that ability.

Link to comment
Share on other sites

No script, no field – in which context do you use that calculation to create the $var?

 

Eos, I was experimenting with the technique that I read about (that if I recall correctly was credited Josh Ormond) where a local variable could be created in the conditional formatting of a text object and then used to display the result. What I found interesting about the technique was that it provided a way to display calculation results in the GUI file (when using the data separation model) without using scripts or creating a calculation field in the data file.

Link to comment
Share on other sites

You are probably referring to Matt Petrowsky's YouTube video.  Keep in mind, there are times, that it will not evaluate and refresh the way you think. It's very prone to breaking if not set up correctly. And the application of what Matt and I communicated about is primarily UI elements. If you are putting mission-critical data up on the screen, use a tried-and-true method.

Link to comment
Share on other sites

I believe this ( $local variables ) still actually shows as a merge variable depending on how/when you set it, though it works a little different than before. But I agree that you really have to be careful with there use. Most of the time I use them, it's strictly for UI elements. As Wim's article nicely points out, use the variable they way they were intended. And save yourself some headaches. lol

I can confirm that local merge variables do still work, but they appear to me to work the same way they always have.

I haven't had a good excuse to try this before now, but object hiding calculations can also be used to set local merge variables, and they're less sensitive to one of the issues that made the conditional formatting version problematic. When setting merge variables with conditional formatting calculations, it was important to make sure the conditionally formatted object was below the object with the merge variable so that the variable was set before it was drawn. The quick test I ran for myself just now seems to show that we don't have to worry about that with object hiding calculations — the object that sets the variable with its object hiding calculation can be anywhere in the layout's z-order relative to the layout displaying the merge variable, and it still works fine. My guess is evaluating object hiding calculations must be one of the very first steps in drawing a layout. You can also have the object with the hiding calculation be a block of text that is conspicuous in layout mode with something like "This object sets local merge variables", have the hiding calculation always return True (1) as its final result, and we don't have to be as particular with how the object is formatted and where it's located.

I feel the need to play devil's advocate against a pendulum in this thread that was swung all the way in one direction, that direction being "don't use local merge variables". There are other schools of thought and other reasonable balances of priorities.

Setting variables with a Let function in a calculation rather than in a script step is inconsistent with the primary mental models of what calculations and scripts steps do and how they relate to the rest of an application's logic. Calculations typically follow a functional computation paradigm, and script steps do imperative computation — calculations return results, scripts actually do things. Setting a variable that persists outside of the calculation and its results is a side-effect of the calculation evaluation, which is inconsistent with the functional paradigm we normally associate with them. One thing to keep in mind in this discussion is that that distinction is purely for the benefit of human developers. The computer doesn't care. Different developers working on different projects can and do have different priorities that lead to different practices, and life would be boring any other way. It can be reasonable to break with the traditional dichotomy between calculations and scripts if your mental model is different (less defensible in a tool as "controlled" as FileMaker), or to achieve a result that would be much more complicated or slower otherwise (more defensible).

There's a similar, but distinct, tension between prioritizing different programming paradigms in FileMaker. Some folks favor as much declarative programming as possible: try to solve any problem with calculation fields, relationships, or any other feature that FileMaker will resolve on its own as appropriate. On the upside, this takes advantage of a lot of functionality that FileMaker gives us with a minimal amount of additional labor invested. A more subtle advantage is that the definitions of how entities relate to each other (in the documentation sense) can also serve as the programming to make the application that users experience actually work. The downside is that declarative programming in FileMaker is dependent on FileMaker's decisions about when to update things. Sometimes FileMaker doesn't refresh things when we'd like it to, resulting in issues like displaying out-of-date information. Sometimes FileMaker refreshes things when we'd prefer that it didn't, resulting in performance problems. Shifting more functionality to a more imperative model — more scripting — takes over more control of when operations happen, at the cost of being responsible for making sure those operations happen. Setting layout merge variables with an object hiding calculation is closer to the declarative paradigm. Whenever FileMaker gets around to drawing the layout, your variables come along for the ride.

Yet another issue is the relationship between objects and the programming logic that manipulates those objects. Keeping those two closer together usually makes software easier to understand. Further, minimizing the scope that variables apply to is associated with thinking about logic in cognitively-tractable chunks small enough to fit in human short-term memory — using smaller-scope variables limits how much you have to keep in your head at any one time while developing a solution. This could be used to argue both for and against setting merge variables with a hide object calculation. Setting a persistent variable (local or global) increases the scope that we otherwise deal with in the Let function. On the other hand, setting and using a local variable in the layout constrains the variable to a smaller scope than setting a variable in a script and displaying it in a layout. (Local variables set by a layout appear to not be accessible in a script.)

Developers often like to use generalized code, using the same logic for several different situations. It has the potential to be great, and it's a fundamental part of programming, but it's often oversold. Generalization can often (but not always) come at the cost of making logic more difficult to follow by separating objects from the logic that manipulates them and being less optimal for specific situations. (No matter how good you think you are at generalized thinking, you're better at more concrete thinking. I can point you to science that says so.) We've seen the argument that you should program similar functionality to use the same code so that you only have one place to make any changes to that logic that come up in the future — but this doesn't really hold water. It's just as plausible (and, in my experience, probable) that the logic will need one change for some places it gets used, and no change or a different change for other places it gets used, so the benefit of the early extra effort to write the logic in generalized form can be diluted or even just wasted. (Yes, writing generalized code takes more effort than case-specific code. Again, science says so.) Resist premature generalization.

  • Like 1
Link to comment
Share on other sites

I have attached my test file that I have been playing around with. It's a data separation example using Local Merge Variables. I am curious to see if anyone can figure out a way to display the metadata (without adding any new fields to any of the tables) so it displays both the description and the value of (phone, email, etc.) as shown in the screen capture:

 

 

16376858765_a2bcb7af36_o.png

Contacts-Local_Merge_Variables.zip

Link to comment
Share on other sites

 I am not using it in a script. I am using it on a layout to display data...so I do not have to create calculation fields solely for presentation purposes.

 

In addition to all of the above, you should also be aware that local $variables declared while no script is running cease to exist while any script is in progress. So the  layout with merged $variables will not display the same while a script is paused, for example (unless it remains unrefreshed for the duration).

Link to comment
Share on other sites

Appreciate the input Jeremy. I haven't used them much since, I believe 12v2. Around that time, it broke. I had to scramble to fix it in my solutions. I am glad to hear ( and test ) that the "Hide object when..." calculation is so much more reliable.  

 

There are a few caveats that come with using a Local Variable though. They do NOT function like Global variables. As comment pointed out, they don't exist during scripts. You can pass them as variables, but it's the only way to capture their values in a script.

 

Also, it's not multi-record friendly. So you can't use them to display different values in different records, unless you create multiple variables. 

 

Halburn - you could also use a WebViewer.

Link to comment
Share on other sites

In addition to all of the above, you should also be aware that local $variables declared while no script is running cease to exist while any script is in progress. So the  layout with merged $variables will not display the same while a script is paused, for example (unless it remains unrefreshed for the duration).

 

Are you sure? I tried futzing with it for a few minutes, and I couldn't get the merge variable set by an object hiding calculation to go away. I could get the script to overwrite the variable, but the script-written value would only display if the object hiding calculation respected existing content in that variable. Pausing a script apparently triggers re-evaluation of hide calculations and drawing the merge variables. I added a $counter and found that running a script does indeed wipe out the original local variable values, but that didn't stop the hide calculation from setting new ones when the script paused. I did confirm that the script doesn't see what the merge variable value was before the script ran, presumably because it doesn't exist again until parts of the layout re-draw, and the script does see the contents of the variable after the layout re-populates it. Is there a more specific situation where merged variables don't adequately update during script execution?

 

In the course of this futzing around, I also found that $local variables set by a layout appear to be available to other windows. This doesn't necessarily mean window-specific merge variables can't be done. Whatever process draws the merge variables appears to always evaluate the hide calculations on the layout first. Merge variables set to the window name work how I hoped they would work. This drawing order makes intuitive sense to me; if I were drawing a layout, I'd want to start by making sure I didn't draw anything I didn't have to.

 

Josh, I concede that merge variables set by hide calculations in a list view is a problem. Merge variables don't support repetitions, and we don't have a way to change the repetitions by record in the merge variable if we did. Record-specific merge variables can still be done with the more finicky conditional formatting calculation as long as we remember to get the z-order right. And I found that, at least in my copy of FileMaker 13v4, these variables also seamlessly re-draw during a paused script.

post-103690-0-51147800-1422329874_thumb.

post-103690-0-38426100-1422329939_thumb.

Link to comment
Share on other sites

I did want to clarify my post a bit (although we're all far past it now) ... I meant using $ in a merge variable was no dependable like it was in 11, triggered by conditional formatting.  As you, Josh, we were using it extensively within Let() within conditional formatting attached to objects (refreshed by stacking order and sometimes nudged by placing under a portal edge or field to force refresh).  Usually all it took was being lowest in stacking order.

 

FMI made a lot of changes in refresh and layering between 12.0v1 and 13.02.   I haven't tested recently (haven't had the need) so I am pleased to hear it is viable option even if it doesn't refresh quite as well;  in fact, I have a few posts with files which behave one way on merge variables in 11 and differently in 12 and then also between 12 and 13.

 

So little time and so much to test.  

Link to comment
Share on other sites

One thing you can do, since your $fullName DOES evaluate, move all of the variables calculations into it as a single variable and place only the single variable of <<$FullName>>.  Then they all refresh.  I don't show repetitions in your file at all, something like:

Let ( 
	$FullName = CONTACTS::first_name & " " & CONTACTS::last_name 
	& ¶ &
	List ( METADATA::value )  
	& "¶¶¶" & 
	ValueCount ( List ( METADATA::value ) )
;
""
)

I'm unsure why you want to Count your related text fields, I guess to see what is not empty but ValueCount() does that since you are using List().  Anyway, maybe it'll move you forward a bit.  

 

added word 'not' in blue

Edited by LaRetta
  • Like 1
Link to comment
Share on other sites

One thing you can do, since your $fullName DOES evaluate, move all of the variables calculations into it as a single variable and place only the single variable of <<$FullName>>.  Then they all refresh.  I don't show repetitions in your file at all, something like:

Let ( 
	$FullName = CONTACTS::first_name & " " & CONTACTS::last_name 
	& ¶ &
	List ( METADATA::value )  
	& "¶¶¶" & 
	ValueCount ( List ( METADATA::value ) )
;
""
)

I'm unsure why you want to Count your related text fields, I guess to see what is not empty but ValueCount() does that since you are using List().  Anyway, maybe it'll move you forward a bit.  

 

added word 'not' in blue

 

Thanks for your example LaRetta.

 

My demo file is not meant to practical, it's just a test file that I created so I could learn more about the Let function and using this Local Variable technique.

Link to comment
Share on other sites

I believe that 'what you learned' is that it can be used, can be effective, can be difficult to force-refresh at times, and even can change how it refreshes as we move through versions.  However, there ARE principles behind each 'fix' which can be important and, after all, you DID ask for a fix for a refresh issue, right?

 

13 now appears to be stable in refreshing and layout rendering.  The principle is that you can use other layout objects to force a refresh - there are many things you can do.  The question is ... do you want to use these techniques if it is critical the data display properly or timely (according to your business rules)?  Just displaying a client's address is no big - other things such as Past Due notices are another. 

Link to comment
Share on other sites

However, there ARE principles behind each 'fix' which can be important and, after all, you DID ask for a fix for a refresh issue, right?

 

Actually, I was not the person that brought up the refresh issue. This was not a problem for me. I was just trying to get a little bit of experience with the Let function and I thought that I would kill two birds with one stone and experiment with this technique that I had read about a while ago for using the local variables in layouts.

 

Thanks again for your feedback and advice.

Link to comment
Share on other sites

Are you sure?

 

Am I sure of what? That local $variables declared while no script is running are suspended for the duration of any script runtime? Yes, I am sure of that. That a layout showing these variables will necessarily show them as undefined while a script is in progress? No, I can't be sure of that, because that depends on how exactly the layout is set up. Basically, there are three options:

 

[a] the layout is not refreshed, and continues to display the variables as populated before the script was initiated;

 

the layout is refreshed, and the variables are shown as missing;

 

[c] the layout is refreshed, and the refresh causes a new set of variables (with the same names) to be declared and displayed.

 

I suspect you were experiencing [c] when you "couldn't get the merge variable set by an object hiding calculation to go away". What really happened is that the variable did "go away" and a new variable took its place for the duration of the script.

Link to comment
Share on other sites

I've played with these a lot over the past few years. This is what happens.

 

$variable set via Let function outside of the script.  Will only show once the layout is refreshed, or resized, or a script with a freeze window completes ( anything that causes the layout to redraw ).

Start script.

$variable 'disappears', and is not accessible to the script, unless you passed it as a parameter. ( you should be able to watch the data viewer, the layout isn't refreshing so it will stay on the layout ).

Script completes.

Original $variable returns.

 

During the script you can set that same variable to anything. When the script ends, that $variable in the script clears, and the original $variable returns.

Link to comment
Share on other sites

That's the script 0 concept...  any local variable declared outside of a running script is really in this "Script 0".  That explains the normal behavior of the variable resetting when you run another script "Script 1" and when it ends you return to "Script 0"

Link to comment
Share on other sites

That's the script 0 concept...  any local variable declared outside of a running script is really in this "Script 0".

 

That's a potentially misleading formulation. "outside of a running script" suggests that only $variables declared inside of a running script belong to that script. That is not so: any $variables declared during the runtime of a script - no matter how - are in that set.

  • Like 1
Link to comment
Share on other sites

Basically, there are three options:

 

[a] the layout is not refreshed, and continues to display the variables as populated before the script was initiated;

 

the layout is refreshed, and the variables are shown as missing;

 

[c] the layout is refreshed, and the refresh causes a new set of variables (with the same names) to be declared and displayed.

 

I suspect you were experiencing [c] when you "couldn't get the merge variable set by an object hiding calculation to go away". What really happened is that the variable did "go away" and a new variable took its place for the duration of the script.

 

I'm pretty confident that option C is definitely what FileMaker does. This is perfect, as it's the option that makes use of local merge variables most palatable. Merge variables definitely do display while a script is running, and different values can display in response to the script. One more quick test does show one behavior worth keeping in mind for anyone taking advantage of this: the layout-based calculations do not necessarily re-evaluate and reset the merge variable values after the last script exits. So if you want merge variables in a layout to reset after a script is done, the script should do the resetting before it exits.

Link to comment
Share on other sites

What do you see with this file.

 

The $var is set via the "Hide when...". The script sets the same variable. If you watch the data viewer, at least what I'm seeing on my laptop ( Windows 7.1 ) is that the $var resets to the original value. Not creating a new variable with a freshly calculated result.

Variable Test.zip

Link to comment
Share on other sites

I'm pretty confident that option C is definitely what FileMaker does.

 

I'm afraid you misunderstood me (or I you). Filemaker does any one of the three options, depending on how you set this up.

 

 

So if you want merge variables in a layout to reset after a script is done, the script should do the resetting before it exits.

 

I am not sure how that will work, given that before the script exits, the script is still running...?

Link to comment
Share on other sites

That's a potentially misleading formulation. "outside of a running script" suggests that only $variables declared inside of a running script belong to that script. That is not so: any $variables declared during the runtime of a script - no matter how - are in that set.

 

Agreed, that is a better formulation.

Link to comment
Share on other sites

This topic is 3356 days old. Please don't post here. Open a new topic instead.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.