• Empty strings should not be used within calculations. A blank custom function named "Null" should be created and used for empty values (*see note below)
    Within solution code this increases code readability and distinguishes literal values and escaped text from empty values.

    Case (
    	If ( PatternCount ( Table::fieldName ; Tab & Tab ) ≥ 1;
    		"Awesome use of indentation!";
    		Null
    		);
    	If ( PatternCount ( Table::fieldName ; Tab ) ≥ 1 ;
    		"Good code should be easy to read";
    		Null
    		);
    	Null
    )
    // Note the above function uses another custom function
    // which simply returns a tab character in order to improve
    // readability
    

    good

    Case (
    	If ( PatternCount ( Table::fieldName ; "	" & "	" ) ≥ 1;
    		"Awesome use of indentation!";
    		""
    		);
    	If ( PatternCount ( Table::fieldName ; "	" ) ≥ 1 ;
    		"Good code should be easy to read";
    		""
    		);
    	""
    )
    

    bad

  • Here's a series of test cases for clarification - the subtle differences in FM between Null, reserved word False and "" (literal empty string):

Expression

Result

Notes

Null

 

Result is empty.

GetAsBoolean ( Null )

0

GetAsBoolean returns 0/1 for expression results. 

not Null

1

The boolean inverse of Null, or 1.

Null = 0

0

False: This is boolean false because FM does not evaluate empty to reserved word False (0).

GetAsBoolean ( Null ) = 0

1

True: The explicit FM boolean interpretation of Null is 0. 

GetAsBoolean ( Null ) = False

1

True: A different adaptation - FM reserved words True and False are evaluated to the boolean 0/1 values, so 0 = 0. 

Null = False

0

False: This is the most interesting case of all. Null is technically empty and False evaluates to boolean 0, so they are not equal. 

Null = ""

1

True: The literal empty string "" evaluates the same in FM as the empty return of Null.,

Exception Note

Code within a custom function can and should use double quotes for empty values "". This decreases custom function dependencies on the "null" function - making them more portable for copy-pasting.

  • No labels

34 Comments

  1. Anonymous

    When developing leaving "" in the calc allows easier addition of results that may not yet appear apparent.

    Standards should not slow down speed of change of code

    1. I'm not exactly clear on how using null (a custom function) vs. "" impacts speed.

      The only point I can think you are making is that you would simply insert a literal within the quotes?

      Otherwise, if you're editing the calc in the future, regardless of using "" or null, you're going to be replacing text no mater what. Help me understand here.

  2. I propose never, under any circumstances, using the If() function, and using only Case()

    1. It's great to make a proposal. What's the reasoning?

      In most languages, If is there because it's a true Boolean switch. Otherwise you use a Switch statement (which is what the Case statement is)

      They both have a functional use. If is "either or" and Case is "if then else if". I personally would need a good argument to say that not using If is a good standard.

    2. I always use Case() where an If() will suffice, myself; but I don't think it's a big enough distinction to standardize on. Not using If() doesn't do anything to make code more portable, easier to read, or faster to execute. In a world where If() were more exotic, like Gamma(), sure; but developers really should know an If() when they see it.

      1. I'd like to make the argument for If from the readability standpoint. Reading If() means I'm only going to see two options.

        Reading Case() sets me up for the expectation of more possibilities, plus a (typically expected) default.

        Another consideration, which is also used in other languages is a single result If, where you have something like this.

        "You're foo " & If ( Tablename::field = "foo" ; "bar" ) & " my friend."
        

        as opposed to...

        "You're foo " & Case ( Tablename::field = "foo" ; "bar" ) & " my friend."
        

        Yeah, you can use Case, but it doesn't read as well to me.

        1. Anonymous

          I had a similar discussion a couple weeks ago in an German FileMaker forum (http://www.filemaker-magazin.de/forum/beitrag/101760).

          Some developers use exclusively Case(). The main reason was, in older versions of FileMaker you could omit the else part only in Case() but not If(). But the argument does not apply to FM11 anymore.

          I agree with Matt and other developers, If() indicates a dualism, even if the if statement does not include the else part.

          If( $foo = TRUE; "foo" )
          

          On the other side, a case statement might have only one entry, but it indicates, that more possibilities could follow:

          Case( $color = "red"; "something" )
          

          The use of If() and Case() accordingly will increase immediate insight when reading the code.

          Arnold Kegebein
          arnold (at) kegebein (dot) net

  3. I don't like the idea of using a 'null' custom function. It solves a problem that didn't need solving, and reduces the ability to copy and paste code into files that might not (yet) contain that function. Also, it would start a debate on whether to call that function null or empty or nothing or vacuum, etc. One of the standards we should strive for is not to solve problems that are too small, and I think this is one.

    1. These documented standards are not here to become a debate. They can be adapted, borrowed from and modified to heck - as long as it's done somewhere else. The semantics around the use of a given word is not the goal.

      There will be INFINITE arguments about using "null" vs. "void" vs. "empty" vs. whatever...

      The goal is A standard for those who choose to adopt it. (wink)

      So the real question becomes "Is 'null' not obvious in terms of what it means or does?" If not, then we can change it, otherwise it's a standard - not a debate. (smile)

      Here's the definition of "null"

      having or associated with the value zero.

      • Mathematics (of a set or matrix) having no elements, or only zeros as elements.
      • lacking distinctive qualities; having no positive substance or content : his curiously null life.

      as taken from the Mac Dictionary app

    2. Additionally...

      reduces the ability to copy and paste code into files that might not (yet) contain that function

      This is not a primary goal within these coding standards framework. If the time can't be taken to copy or simply create an empty custom function named 'null' for the sake of clean and consistent code, then I'd argue that the developer has other issues.

      One tip that may help others when copying code is to open the Import.log file in a text editor that will update when the file is updated. When copy/pasting code, you can easily see what errors are generated when pasting in code. If you get errors, simply delete, fix and recopy/paste to adjust. This sequence is minor when you reflect on the readability of the code above.

      1. "If the time can't be taken to copy or simply create an empty custom function named 'null' for the sake of clean and consistent code, then I'd argue that the developer has other issues." I don't find this convincing. I think it's perfectly reasonable and professional to use implementation ease and time or robustness to thoughtless copy & paste as a yardstick (one of many) to judge prospective conventions and best practices. (This impulse is one of the few accurate applications of Occam's Razor I've seen recently.) Readability, the motivation for the Null custom function, is another. There isn't a consensus what balance to take between these competing concerns; but I prefer to eat my cake and have it, too, if I can help it. I find myself these days using this ...

        /* Null */ ""

        ... which both signals my intention, and spares me from having to remember yet another dependency each time I copy code between solutions.

        1. Anonymous

          This looks like a great cake-and-eat solution, Jeremy, thank you!  I have been using Null (CF) for consistency, but also have been annoyed by the dependency situation when I copied functions into a different solution.  This meets both.

          I tested against Perrin's table, just out of curiosity, and "" behaves exactly as Null does in all the test cases defined in the table, so it looks like a great solution.  I'm going to adopt this myself now.

          -Matthew Miller

          1. "which both signals my intention"... I beg to differ.

            I think we'll have to disagree on this one. While I can FULLY appreciate the minimalist and decoupled intentions of the proposal, this simply reads "wrong" to me. I'll explain why.

            While a custom function named Null "seemingly" serves no real purpose and only "seems" to provide the value of more readable code, the dependency - which, please do me the favor of thinking about - is actually quite minor - as opposed to the "yet another dependency" proclamation of this being added to some set of hundreds of dependancies.

            I think this is more of a quibble towards a purist objective of dependency-less (yes I may be making up that word) code.

            If you start any solution with a Null function included or simply tell yourself "Before I copy/paste anything I'll make sure that Null is there." then this issue is moot. Seriously, how many seconds of time does it take to add it in once you notice it's not there and needed. Is clicking create within Custom Functions and typing the word Null THAT painful?

            To quote Todd

            "Adding a dependency should provide clear value over solutions that don't require dependancies"

            Ok, here you go.

            If ( Customers::firstName ≠ Null ; "Thank you \"" & Customers::firstName & "\"" ; "Thank You" )
            If ( Customers::firstName ≠ /* Null */ "" ; "Thank you \"" & Customers::firstName & "\"" ; "Thank You" )

            Why then... when I scan the second example does my brain start looking for the comment and my eyes do a double-take when there is no comment to be had? You're now left with having to "explain" that this convention is how you indicate a comparison against an empty. The /* */ is an explicit declaration which says "Hey, here comes something you may need to know about". So, (to me) you are now "confusing" the code with the comments for the mere reason that it's too much to have a custom function named Null? Hmnnnn....

            If I saw this within PHP or Java code I would just sigh and say, "Well this sucks to read".

            I don't buy it, and I would much rather read the former than the later.

            I can easily pick out the not equals, the comparison against nothing and the quoted field due to the backslash quote combination.

            Again, I don't think this type of dependency is a deal breaker. Yeah, maybe it's a bit of hassle when you accidentally forget to copy it into a solution, but you're only going to make that mistake a few times - I would hope.

            Please do note the Note in the body of the page. Null is for solution code - scripts and calcs - not for other custom functions. I find the advantages outweigh any minor inconvenience. Scripts and calcs are referenced often, but custom functions, once added are rarely ventured into.

            P.S. Yes, I would probably opt for the following

            If ( IsEmpty ( Customers::firstName) ... but I needed an example and it's what came to mind first.

            1. A purist objective of Occam's Razor vs. a purist objective of readability — six of one, half dozen of the other. Neither approach is very inconvenient for developers familiar with the convention. Doing a double-take reading /* Null */ "" might take a second or two, and a copied & pasted script dependent on a missing Null custom function might take tens of seconds to diagnose and fix. No big deal.

              If you're a competent developer who is not aware of FileMakerStandards.org (a demographic and model of our future selves I've argued in the past is worth pandering to), it's a very different story. Doing a double-take reading /* Null */ "" still might take a couple seconds. Pasting a script, finding that it doesn't work, finding the commented-out calculation, finding that it's commented-out because there's this "Null" thing the new file doesn't have, going back to the source of the script to see what it is and what it does, taking a few seconds to let that sink-in, deciding whether to reproduce it or re-write calculations to not use it, and adding the custom function to a new file — this sequence is substantially more inconvenient and pulls a developer much further off track. Maybe this still isn't so bad, but this is an inconvenience I think we should be considering. We might argue that a developer who is not familiar with our conventions copying code that follows our conventions is improbable, and therefore this scenario is unimportant when calculating the expected values of competing candidate conventions; but I think any convention will be more robust if we don't invoke probabilities that we can't really know.

              P.S. The fact that a toy example is what comes to mind first is not a good sign that there's much at stake with this particular convention. A more significant issue might have a more pressing example fresh at hand to demonstrate arguments either way.

              P.P.S. I don't consider /* */ to be reserved for "here's something you may need to know about", but I find myself using it just as often for "in case this looks confusing, this is what I'm doing here". (I do this especially often with math-heavy code where I don't expect a fellow developer to know the math, or a mathematician to know FileMaker.) It may be better to write code readable enough for such a clarifying comment to be unnecessary, but sometimes expediency wins.

            2. Honestly I prefer this.
              If ( Customers::firstName ≠ "" ; "Thank you \"" & Customers::firstName & "\"" ; "Thank You" ).

              This is may not be the thread to bring this up on, but it is a good example of the difficulty I am having using these standards.

              I have been trying to use these standards in systems of different sizes over the past several months, and I find I just can't stomach the layers of dependancies.  It just doesn't fit into how I want to build. I want a much more loosely coupled set of modules which I can assemble quickly and easily into solutions.  Put only whats required in there and nothing else.  Even if it is easy to copy 50 custom functions into a solution, I don't want to do that unless it absolutely necessary.  Its just cruft waiting to happen.

              I also want code that can be easily shared with the widest numbers of people. This is how you get high quality code. Its also how you improve the ecosystem of easily available code.

              I realize now that this is NOT really the goal of this project. This projects goal is to be more a monolithic set of standards and practices. Its not a framework like Focus from beezwax, but it still encourages tightly coupled approach.

              I think that Matt and some of the others driving this project have the opinion that this is the best approach to take with FileMaker. I don't think that is crazy, or stupid. In fact this project maybe the best expression of the traditional FileMaker way.

              However I believe that you can write code in a more modular way, that you can reduce or eliminate dependancies, and that there is tremendous value if you do.

              Just as an example. I can write parameter passing code that uses the same format as the very convenient # custom function set, without using the custom function.  If I want to create a module to share why would I burden my users with an unnecessary dependency.  What if some of my users already had their own CF for passing multiple parameters?

              The standard should be that multiple parameters are passed using Let Notation. Period the end. Implementation should be left to the developer.  And if your planning on sharing your module, then you should NOT use a custom function.

              I guess my point is this I want to work at a different level for a while.  Rather than trying to build an entire framework or a tightly coupled set of standards, I want to focus on breaking up my apps into more modular components or modules.  I want there to be the fewest number of dependancies as possible between the modules. I want portability to be a high priority

              How the modules get assembled into a complete solution is a different question, which I am going to leave alone for a while.

              I think work should continue here. Its valuable important work for those who want to take this approach, and for those who don't.  I would like my modules to work with people who adopt these standards. And if I do a good job they will! (smile)

              1. I agree with Todd that minimizing dependencies is worth pursuing. I also agree with Matt that maximizing code readability is worth pursuing. If this project's goal is to be a monolithic set of practices, that isn't necessarily a disservice to modularity and portability. To realize the theoretical value of modular code, modules must be intercompatible. If we're going to steer people towards creating intercompatible modules following the same conventions, I think a steady gentle hand of orthogonal conventions will be more effective at winning friends than a collection of all-or-nothing standards. I don't think we have gathered a set of all-or-nothing standards so far — for example, references to the Null custom function are discouraged in other custom functions — and I'd like to keep it that way. I've never regarded or advertized the conventions as monolithic or tightly coupled — I pick and choose, and I don't know anyone who doesn't.

                By choosing to work in FileMaker, we already limit the portability of our code to other folks who also use FileMaker, which is a big leap in a world with so many open source tools with larger communities. In this light, limiting portability to within the scope of less or more constraining standards seems somewhat arbitrary to me without some cutoff point of how portable we want our code to be or some point at which other concerns become more important. If we just wanted to maximize the portability of our code, we'd be using JavaScript instead of FileMaker. Jonathan Stark took that path a long time ago.

                Most folks who choose FileMaker choose it because they can build, deploy, and change custom applications quickly and easily. Standards should facilitate that, make it quicker and easier to build custom applications well. Anything that makes development or customization slower or harder in the name of portability invites heavy scrutiny. Less portable code invites its own scrutiny, but we're frequently willing to overlook that if it makes it faster or easier for us to write custom applications, like the # functions. We seem to disagree whether the Null custom function makes programming easier or faster enough to justify the decreased portability of code using it, but it does clearly make code less portable, even if only slightly so. I think it's better to choose conventions based on information we agree on than information we don't.

                1. I've never regarded or advertized the conventions as monolithic or tightly coupled — I pick and choose, and I don't know anyone who doesn't.— I pick and choose, and I don't know anyone who doesn't.

                  Weather you have regarded them or advertised them that way or not doesn't change the fact that if you tell people that best practice is to use a Null custom function all over their code you are creating an unecessary dependency which is almost the definition of tightly coupled. 

                  By choosing to work in FileMaker, we already limit the portability of our code to other folks who also use FileMaker,

                  I think the reason we haven't developed better practices around portability is simply because we were trained by earlier versions of FileMaker to do otherwise.  I believe it is time to move beyond that thinking.

                  Anything that makes development or customization slower or harder in the name of portability invites heavy scrutiny.

                  Of course it is always a balancing act.  But that is true in any other language. It is always harder to write something for portability then it is to just one off it. But clearly there is a reason why people do it.

                  Your assumption is that it is much much much harder or slower to write portable code. What if it is wasn't that much harder? What if it just required changing perspective?  And even if it did take longer to write portable code, the easy availability of other portable code from other developers, or from your own library would more than make up for any loss in productivity.

                  ...without some cutoff point of how portable we want our code to be or some point at which other concerns become more important.

                  If your saying that we don't have any clear cutoff for when other concerns become more important, then I would have to agree with you.  But again I think that's mostly because we haven't yet tried. We need more experience, more examples.

                  To that end, I have developed several modules that I believe to be quite powerful and and quite portable.  And I believe that it is possible to take it farther if we can get input from other developers. I have a session scheduled at Pause to show what I have so far and to gather more input and ideas.  After that I intend to present the next level of the work at DevCon.

                  We definitely need some better understanding of where the lines are drawn.  Not everything can or should be modularized.  But that will come.

                   If we just wanted to maximize the portability of our code, we'd be using JavaScript instead of FileMaker.

                  This same problem exists in JavaScript. There is new movement in JavaScript to try to reduce fragmentation and reliance on things like jQuery. Check out this blog post, which is gaining some traction.

                  http://tjholowaychuk.com/post/27984551477/components

                  So it isn't just a FileMaker problem

                  Anyway... I will take this conversation elsewhere.  The goals of this project are valuable, despite my desire to move in a different direction.  No need for me to continue to muddy the waters.

                  (smile)

                  1. Your assumption is that it is much much much harder or slower to write portable code.

                    Not at all! I think it's faster, easier, and more portable to use /* Null */ "" than a Null custom function, for example. My point is only that we have many factors to balance, and portability does not dominate other concerns. I did not intend the comparison to JavaScript to claim that JavaScript didn't have it's own framework lock-in examples, but that the choice of FileMaker as a platform implies that scope of code portability is probably not the top priority of whoever made that choice.

                    For modules to be intercompatible, and therefore useful towards the goal of modularity, they have to play by the same rules, i.e., conventions and standards. I think that makes FileMakerStandards.org the perfect place to have this conversation, whether portability is an effect the existing conventions or not — especially if it's not. Where the conventions don't make code more portable — whether by making the most useful techniques the most ubiquitous or by Occam's Razor — the conventions fail. Quite the opposite of muddying the waters, I think this conversation forces us to clarify priorities. Please keep the conversation here going, or if you take it somewhere else, take me with you!

                    1. For modules to be intercompatible, and therefore useful towards the goal of modularity, they have to play by the same rules, i.e., conventions and standards.

                      I agree that there should be a low level set of standards. I keep using Let Notation as a good example.  But the vast majority of what is recommended here goes far beyond that.

                      I think that is because the Matt and others have a different goal.  The vigorous and also reasonable defense of a Null CF proves the point. It's ok to have a different goal.  They aren't even mutually exclusive. Its a matter of priorities.  

                      I should let Matt speak for himself, but it seems that Matt cares less about adoption and more about just getting a good solid set of practices that work for him and for others. This isn't a bad goal. I just believe that it would be better if there were less standards that were more easily adoptable.

                      In the PHP world, both types exist.  There are plenty of monolithic frameworks, like CodeIgniter for example. And there are also a few loosely coupled sets of libraries.  The two most well known are ZendFramework and Symfony.  So it's clear that an ecosystem can support both types.

                      But I think its work pointing out that Zend and Symfony are the most highly regarded. Because of the high code quality and because each individual module can be used by itself or hooked into any other framework, without slapping in a bunch of unwanted dependancies. Thats the reason why Drupal 8 has adopted a few of the Symfony components. 

                      But I really am going to shut up now.  Its not fair of me to keep taking swipes at the efforts here when I haven't yet shown a single example of my own ideas.  (smile)

                      1. Ok, you monolithic, framework, standards-based, toy example razor salesmen. Let's move the conversation. (wink)

                        Follow now at FileMaker & The Great Null Dilemma

      2. I have to agree with Jeremy here. I don't buy this at all. It makes good sense to keep your dependancies to a bare minimum. Especially in an environment like filemaker where maintenance of dependancies is a headache. 

        Adding a dependency should provide clear value over solutions that don't require dependancies. In my opinion a custom function for "NULL" does not.

        1. I'm of the opinion that FMI should add a built in function named NULL.

          Or better yet, make it a reserved word with a true Null value. The engine is clearly able to handle such a distinction...since other Reserved words are interchangeable with their "value".  For example, True = 1, False = 0, Lowercase = 32, etc.

      3. In the case of TAB, I could be convinced that it was useful since the tab character is not visually distinguishable from other white space.  However I think I would choose to use Char(9) in calc if I needed it.  I'd do it like this.

        Let([

        TAB = Char(9);

        ...

  4. I can see how tab and null could be confused with field names. These should be added to the Reserved elements then.

    1. Anonymous

      I would name these functions with uppercase letters (TAB and NULL) because they act as constant values.

      Arnold Kegebein
      arnold (at) kegebein (dot) net

      1. Yeah, I think this is worth considering. It does mix the UPPERCASE convention used for GLOBALS and $$GLOBAL.VARIABLES and doesn't follow the TitleCase convention for Custom Functions.

        The question is, do we mix case conventions in custom functions or stick with the standard. That would make it Tab and Null.

        Initially, I went with lowercase null because it's easier/faster to type. But it would seem that following Custom function naming conventions might be advised. Thoughts?

        1. Though TAB and NULL may technically be implemented as custom functions, they're being treated as literal values in the same sense as other pre-defined value flags such as "True", "False", "Bold", and "HighlightYellow". FileMaker styles all of these as UpperCamelCase (and re-styles them to UpperCamelCase regardless of how you entered them). I'd rather be consistent with how FileMaker treats constants than how people usually do it in other languages, thus the WWFMD principle: What would FileMaker do? FileMaker would make these UpperCamelCase.

          1. Yeah, I agree too. Let's go with consistency.

  5. Here's a series of test cases for clarification - the subtle differences in FM between Null, reserved word False and "" (literal empty string):

    Expression

    Result

    Notes

    Null

    

    Result is empty.

    GetAsBoolean ( Null )

    0

    GetAsBoolean returns 0/1 for expression results.

    not Null

    1

    The boolean inverse of Null, or 1.

    Null = 0

    0

    False: This is boolean false because FM does not evaluate empty to reserved word False (0).

    GetAsBoolean ( Null ) = 0

    1

    True: The explicit FM boolean interpretation of Null is 0.

    GetAsBoolean ( Null ) = False

    1

    True: A different adaptation - FM reserved words True and False are evaluated to the boolean 0/1 values, so 0 = 0.

    Null = False

    0

    False: This is the most interesting case of all. Null is technically empty and False evaluates to boolean 0, so they are not equal.

    Null = ""

    1

    True: The literal empty string "" evaluates the same in FM as the empty return of Null.,

    1. Very nice contribution. You should add that into the page above.

      This also reminded me why I like using an empty custom function named Null. Since typically, null is associated with nothing, it's a nice benefit when used with Set Variable [] script step.

      Set Variable [ $$GLOBAL.VARIABLE ; Null ]
      

      kills the variable making the test case of IsEmpty ( ) on a variable evaluate properly.

      Running the following script steps reveal that setting a variable to Null is exactly what you would expect.

      Set Variable [ $variable; Value:False ]
      Show Custom Dialog [ Title: "Test"; Message: IsEmpty ( $variable ); Buttons: “OK” ]
      // results in False
      Set Variable [ $variable; Value:Null ]
      Show Custom Dialog [ Title: "Test"; Message: IsEmpty ( $variable ); Buttons: “OK” ]
      // results in True

  6. Anonymous

    You never even have to define it if you use the syntax: $null. Now THAT'S portable.

    jonathan at fletcher data dot com

    1. Null as a custom function is a constant null. Using $null opens the potential for something that is not null. I've seen custom functions like Custom List use a locally scoped $null. I'd much rather be 100% sure what I have is truly a Null - hence the use of the CF.

      1. Anonymous

        Would a $null created in a CF still exist in the calling script? If it did, I see your point.

        jfletch

        1. Yes, it does. Complex Custom Functions which use $localVariables for the purposes of recursion do persist within the same script which called the CF.