Calculation Function Formatting
At Extensitech, we strive to create and adhere to standards and design patterns wherever possible. Our goal is to make our code easy to read and easy to modify, particularly for our own developers, but also for other developers who may encounter our code in the future. Consistent patterns make development quicker, more efficient, and less prone to mistakes. This applies to our naming conventions, patterns in our relationships graph, scripting conventions and more.
Over time, we’ve developed some conventions for writing calculations that work quite well for us. On the other hand, we realize that some of these conventions are very different from how other developers write calculations. For the most part, these conventions don’t change the actual results of functions, but they make them easier for us to read and modify. I’d like to share some of these conventions, and I’d be curious to hear other developers’ opinions about them. Would you agree that our conventions could make functions easier to read? Easier to modify? Are there changes you’d suggest that could make these conventions better? If you were to run across these conventions and try to modify the functions, would you find any of these conventions overly confusing? I look forward to your comments.
Before I begin, let me say up front that we’re not suggesting there’s “one right way” to format FileMaker calculations, including our way. These are some ideas we’ve come up with and adopted over time that work well for us. Hopefully, you’ll find some tidbit that’s useful to you, as well. And perhaps, in turn, you can provide some feedback that will help us to make them better.
We’ve established an internal convention that, when writing calculations, all function punctuation has a space both before and after. For example:
Left ( Table::my_field ; 3 ) & Right ( "some text" ; 5 )
Note the spacing around the parentheses, semi-colon and ampersand. Note, too, that this only applies to function punctuation, not to internal punctuation like the double colon in the field name, or text within a text string.
Our original reason for this was that on Windows, prior to version 14, this meant you could double-click a field and highlight it, without accidentally picking up the punctuation before and/or after the field name. For some long field names and complex functions, this was a real time saver. It felt “odd” to type that way at first, but very quickly it became so natural that I sometimes forget and do the same thing when typing an email, or even while typing this blog!
Unfortunately, we found later that on Mac, the colon is a word break, so double-clicking, for instance the a in “Table” would select only Table and not Table::my_field. This is still easier, since the user can command + right arrow to get the rest of the field name. Sometimes it’s even preferable, when the developer only wants the table name, or a field name that is common among multiple tables.
More unfortunate still, FileMaker 14 for Windows treats semi-colons, underscores and a number of other special characters as word breaks, where they weren’t before. This makes double-click selection much tougher, and I’m still trying to get comfortable with the extra keystrokes. (I’m sure I’ll eventually stop grumbling about this and just get used to it. At least, that seems more likely than FileMaker having mercy and putting it back the way it was!)
Still, even if you’re going to click-drag to select fields from your functions, a little extra spacing on either side makes it easier to avoid picking up that stray parenthesis or semi-colon and inadvertently copying and pasting it where it doesn’t belong. In a long calculation, when you try to save and get an error, finding that elusive stray punctuation you pasted can really slow your roll!
When comparing our calculations to those written by other developers, perhaps the first difference we notice is the difference in where we put the semi-colons if we break a function into multiple lines. Consider the following:
"myFieldObject" // object name
; "bounds" // attribute
; 1 // repetition
; 2 // portalrownumber
Notice that when we break parameters out onto their own lines, we put the semi-colon separator at the beginning of the line, instead of the end.
This tends to make developers from some other languages, and even some other FileMaker developers, go a bit cross-eyed. They’re used to seeing semi-colons at the ends of lines. In spite of being initially jarring, though, I find it’s a useful tool to remind the developer who’s new to FileMaker that semi-colons in FileMaker are not used to denote the end of a line of code. They are separators between parameters of a function.
Still, as with our other conventions, this doesn’t change the result of the calculation, so why not just go with semi-colons at the end, like most everyone else?
It’s not a monumental difference, even for us, but we’ve found that when we want to comment out a parameter with //, or copy a parameter from one part of our calculation to another, it’s seldom the first parameter, and more often the last.
Consider that we’d written the function above, and later decided to comment out the (optional) portal row number. We’d just add // in front of “; 2”, and be done. If we wrote the same function with semi-colons at the end, we’d need to do this:
"myFieldObject" ; // object name
"bounds" ; // attribute
1 /* ; // repetition
2 */ // portalrownumber
Sure, semi-colons at the beginning don’t always work out, either. Occasionally we want to comment or copy the first line, and end up having to comment the separator on the second row. More often, though, the first parameter for a function is pretty specific, and it’s the later ones, and particularly the last ones, that are either portable or optional.
This last section is the newest convention that we’re just now in the process of adopting. It partly addresses the “first line” issue above.
There are three particular situations where a function has repeated sections where the order matters, and may end up changing either while the function is originally being written, or later when changes are required. They are the Let function, the Case function, and strings concatenated with the ampersand. Consider the following calculation, and notice the use of “dummy lines”:
Let ( [
; old_value = $$value_original
; new_value = Table::my_field
; new_value > 100
; "Too High!"
; new_value < old_value
; "Too Low!"
& Get ( CurrentHostTimestamp )
& " - "
& Get ( AccountName )
& " has successfully changed "
& " to "
These three uses of “dummy lines” won’t change the result of their respective functions at all. We don’t actually care what “x” is, the condition 0 (false) will never be met to return a 0, and prepending an empty string to the front of a string produces, well… the string. So why add them? They’re all about being able to insert other lines, or copy and paste lines, without having to adjust for whether the line you’re working with is the first line.
In the Let, imagine that you wanted to change new_value to the Max of old_value or Table::my_field. You’d have to move new_value to be declared first, and without the “x=1” dummy line, that would mean adding a semi-colon to old_value line and removing it from the new_value line. Now, you can copy and paste the lines to swap them and not have to worry about that.
In the Case, let’s say you’ve decided that a new value greater than 100 isn’t too high if it’s still less than the old value. You’d need to move the “> 100” condition and result above the next result. Without the “0;0” dummy line, that would again mean removing a semi-colon from one line, and adding it to another. Now you don’t have to.
In the series of concatenated values, it could be that you later decide the account name should come first. You have a perfectly good line to copy and paste, but without the “” dummy line, you’d have to adjust your ampersands, too.
Is this going to save hours and hours of development any time soon? Of course not! I personally just like that it’s one less annoyance when I’m trying to think through requirements and get things in the right order. Often, I’m so caught up in working through the logic that I end up forgetting the pesky semi-colon or ampersand, and just when I think I’m ready to test I hit OK and find I have an error. Now I’m checking my typing, and getting unnecessarily frustrated, instead of focusing on getting the calculation to return the right result.
Would I use this for every Let, Case and set of concatenated values? I’m still not sure. I’m sure that if I do, it’ll be overkill more often than not. On the other hand, once it’s a habit (and it’s already becoming one) it takes less time to just do it than to stop and think about whether it’s warranted. Furthermore, when it is warranted, I often don’t realize it until I’m pretty deep into the calculation, or even months later when I’ve come back to add to the calculation to meet new requirements. It might just be easier to add it every time out of habit.
What do you think?
Possibly, hopefully, you’ve found something challenging and/or useful in our conventions. I don’t expect that everyone who reads this will find every one of them useful or desirable. Perhaps you won’t implement any of them. I hope I’ve at least caused you to give some thought as to how you write your own functions, and why.
I’d love to hear your comments. Did you find any of this interesting? Do you think you’ll use any of it? Do you think any of these conventions are frivolous, or even detrimental, or too hard to read and/or write? Please, by all means, share those thoughts. There may be no “one right way”, but there are certainly reasons to avoid some ways, and many good ways to explore.
So tell me what you think!