[FMP Standards] Best Practices > Custom Functions » Script Parameter Interface

48 views
Skip to first unread message

Jeremy Bante (Confluence)

unread,
Nov 17, 2015, 3:24:48 PM11/17/15
to fmsta...@googlegroups.com
Jeremy Bante edited a page
 
Change comment: Updating links
page icon
Custom Functions » Script Parameter Interface

...

These functions implement the basics of adding data to and retrieving data from dictionary data structures.

# ( name ; value )

The # ( name ; value ) function creates a name-value pair. A dictionary data structure can be created by concatenating several calls to #() as if they were plain text. Name-value pairs can be nested.

...

By placing the defaults before the actual parameters, any values set by the actual script parameter will override the defaults.

#Assign ( parameters )

The #Assign function parses a dictionary into locally-scoped script variables. The name from each name-value pair is used as the variable name, and the value from each pair is used as that variable's value.

...

The #Assign function returns True (1) if there was no error detected while assigning the values to variables, and returns False (0) otherwise. If there was an error detected, FileMaker's error code is assigned to the $#Assign.error variable.

#Get ( parameters ; name )

The #Get function returns a named value from a dictionary.

...

These functions are less fundamental to working with dictionary data structures than the core functions, but experience has demonstrated that this functionality can be indispensable in practical applications.

#AssignGlobal ( parameters )

The #AssignGlobal function parses a dictionary into global variables.

...

The #AssignGlobal function returns True (1) if there was no error detected while assigning the values to variables, and returns False (0) otherwise. If there was an error detected, FileMaker's error code is assigned to the $#AssignGlobal.error variable.

#Filter ( parameters ; filterParameters )

The #Filter function returns a dictionary containing only those name-value pairs where the name is included in the return-delimited list filterParameters.

...

This function can prevent an "injection" of unexpected variables that might cause problems.

#GetNameList ( parameters )

The #GetNameList function returns a list of names from all name/value pairs in parameters. This is useful when you don't know what names exist, and you want to iterate through all the name/value pairs that exist.

Code Block
#GetNameList (
	# ( "name" ; "value" )
	& # ( "foo" ; "bar" );
)
= List ( "name" ; "foo" )

#Remove ( parameters ; removeParameters )

The #Remove function returns a dictionary containing only those name-value pairs where the name is not included in the return-delimited list removeParameters. This is complementary to the #Filter function.

Code Block
#Assign ( #Remove (
	# ( "name" ; "value" )
	& # ( "otherName" ; "otherValue" )
	& # ( "foo" ; "bar" ) ;
	List ( "name" ; "otherName" )
) ) // $foo is assigned "bar"; $name and $otherName are unaffected

ScriptOptionalParameterList ( scriptNameToParse )

The ScriptOptionalParameterList function parses a script name, returning a return-delimited list of optional parameters for that script, in the order they appear in the script name. This function assumes that the script name conforms to the FileMakerStandards.org naming convention for scripts.

...

Code Block
ScriptOptionalParameterList ( "" ) = ScriptOptionalParameterList ( Get ( ScriptName ) )

ScriptRequiredParameterList ( scriptNameToParse )

The ScriptRequiredParameterList function parses a script name, returning a return-delimited list of parameters required for that script, in the order they appear in the script name. This function assumes that the script name conforms to the FileMakerStandards.org naming convention for scripts. This is useful to generate the argument used by the VerifyVariablesNotEmpty function to validate that all required parameters have values. When the scriptNameToParse parameter is empty, the function will use the current script name.

...

Code Block
ScriptRequiredParameterList ( "Script Name ( required1 ; required2 ; { optional1 ; optional2 } )" )
= List ( "required1" ; "required2" )

VerifyVariablesNotEmpty ( nameList )

The VerifyVariablesNotEmpty function returns True (1) if each of the parameters in nameList has been assigned to a non-empty variable of the same name. The function returns False (0) if any variable defined by nameList is empty. This is useful for verifying that each parameter required by a script has been successfully assigned before proceeding.

...

These functions are documented for backwards compatibility. These functions are all equivalent to simple combinations of other functions, which can make the functionality more self-describing, especially for developers who may be unfamiliar with them.

#AssignScriptParameters

The #AssignScriptParameters function will assign all named values in the script parameter to local script variables of the same name. If any parameters indicated as required by the script name are empty, the function returns False (0); the function returns True (1) otherwise.

...

This approach enables greater flexibility in defining what variables are required and how those variables are assigned.

#AssignScriptResults

This function is exactly equivalent to this calculation:

Code Block
#Assign ( Get ( ScriptResult ) )

#GetScriptParameter ( name )

This function is exactly equivalent to this calculation:

Code Block
#Get ( Get ( ScriptParameter ) ; name )

#GetScriptResult ( name )

This function is exactly equivalent to this calculation:

...

View page Icon View page
Add comment Icon Add comment
Like Icon Like
 
Stop watching space
Manage notifications
This message was sent by Atlassian Confluence 5.6.6

Matt Petrowsky

unread,
Nov 17, 2015, 4:34:03 PM11/17/15
to fmsta...@googlegroups.com

Jeremy et al.

I know most of us are keeping separate repositories based on the work we do with custom functions. The question is this… “Do we have the will and desire to consolidate these various repositories into a unified repository?”

For example. I’ve made a few changes to the #Assign function which allows it to support weakly typed parameters with missing semi colons because there are times where I don’t use #() to create my parameters.

Do we really want to do patches to individual functions and treat the fmpstandards repo as an island or shall we just do full merges from the forked forked repos.

I don’t know how many personally rely on the fmstandards repo anyway.

Matt

GetNameList (

# ( "name" ; "value" )
& # ( "foo" ; "bar" );

)
= List ( "name" ; "foo" )

#Remove ( parameters ; removeParameters )

The #Remove function returns a dictionary containing only those name-value pairs where the name is not included in the return-delimited list removeParameters. This is complementary to the #Filter function.
Code Block

Assign ( #Remove (

Assign ( Get ( ScriptResult ) )

#GetScriptParameter ( name )

This function is exactly equivalent to this calculation:
Code Block

Get ( Get ( ScriptParameter ) ; name )

#GetScriptResult ( name )

This function is exactly equivalent to this calculation:
...
View page Icon
View page

Add comment Icon
Add comment

Like Icon
Like
 
Stop watching space

Manage notifications

This message was sent by Atlassian Confluence 5.6.6

You received this message because you are subscribed to the Google Groups "FileMaker Development Standards" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fmstandards...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Cristos L-C

unread,
Nov 17, 2015, 4:54:07 PM11/17/15
to fmsta...@googlegroups.com
We’ve actually adopted SFR-encoding rather than #Let-notation due to concerns about `Evaluate()` risk, so we’re not relying on it at all. I do think that it might be useful to know that there’s ongoing development of these functions, though. If not merging the forks back together, maybe a list of forks and their purpose in the README?

--

Ben Graham

unread,
Nov 17, 2015, 5:30:47 PM11/17/15
to fmsta...@googlegroups.com
I have been gradually changing from the Six Fried Rice to the FM Standards.  Although I still have places where the SFR works best so that is #6() to not interfere with #().

I do update my functions periodically with the latest in the repository.

Cheers,
Ben


On 18 Nov 2015, at 8:03 am, Matt Petrowsky <petrows...@gmail.com> wrote:

Jeremy et al.

I know most of us are keeping separate repositories based on the work we do with custom functions. The question is this… “Do we have the will and desire to consolidate these various repositories into a unified repository?”

For example. I’ve made a few changes to the #Assign function which allows it to support weakly typed parameters with missing semi colons because there are times where I don’t use #() to create my parameters.

Do we really want to do patches to individual functions and treat the fmpstandards repo as an island or shall we just do full merges from the forked forked repos.

I don’t know how many personally rely on the fmstandards repo anyway.

Matt

On 17 Nov 2015, at 12:24, Jeremy Bante (Confluence) wrote:


Jeremy Bante edited a page
 
Change comment: Updating links

This message was sent by Atlassian Confluence 5.6.6

You received this message because you are subscribed to the Google Groups "FileMaker Development Standards" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fmstandards...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--

Cristos L-C

unread,
Nov 17, 2015, 6:14:59 PM11/17/15
to fmsta...@googlegroups.com
Ben, would be interested in knowing why you've opted to move in that direction. We're updating a lot of legacy code that still uses fairly fragile Positional parameter-passing, and after reviewing Chris Irvine's article comparing serializers (https://scalefm.com/2015/10/filemaker-serializers-compared/) we're piloting the SFR dictionary as a general replacement. Any insights on why you prefer #Let-notation to SFR would be greatly appreciated!

Jeremy Bante

unread,
Nov 17, 2015, 9:51:03 PM11/17/15
to FileMaker Development Standards
I think of the fmstandards as the authoritative version of the Let notation functions, and I don't really use any of the rest of the repository. I only updated the links because someone on the FileMaker Community forum noted that the current links on the page were broken, and I presumed without checking that they must have been trying to point to the fmstandards fork.

Jeremy

Ben Graham

unread,
Nov 17, 2015, 11:40:17 PM11/17/15
to fmsta...@googlegroups.com
Hi Cristos and all,

I had incorporated the SFR #() function and many of the dict functions into my vertical market solution some time ago. Then later saw that the community and ongoing support for the fmstandards functions were a living growing community network. I also really like the #Assign function and had adopted the Error functions into my solution. I also found the #List and #ListGet functions were quite useful.    I still use the SFR dict functions for a my sync scripts in packaging data for a transactional sync.   I read the article, good stuff!  I am not sure in my use if the risk of injection in regard to the Evaluate() risk is a big concern.  Though I may look more into this.

Cheers,
Ben

Matt Petrowsky

unread,
Nov 18, 2015, 3:51:11 PM11/18/15
to FileMaker Development Standards
Well, then that’s the answer I was looking for. I’ll update the repo
as I make changes to my own fork. I’ll borrow from your personal
repository when applicable.
> --

Jeremy Bante

unread,
Nov 18, 2015, 5:09:19 PM11/18/15
to FileMaker Development Standards
Just please stick to TDD practices when modifying the # functions. That's a substantial part of the reliability that they're known for.

Matt Petrowsky

unread,
Nov 19, 2015, 12:24:57 AM11/19/15
to FileMaker Development Standards
Yeah, sure.
http://david.heinemeierhansson.com/2014/tdd-is-dead-long-live-testing.html

I won’t break things I rely on.

Matt Petrowsky

unread,
Nov 19, 2015, 1:57:42 AM11/19/15
to FileMaker Development Standards
Please see

https://github.com/petrowsky/fmpstandards/commit/c97df1ca905f4c18c424caab613c46cd9b439525

and

https://github.com/petrowsky/fmpstandards/commit/b2e6c86756afa6895076a5ff4ecc54d2de068546

Before you throw out the argument of this adding performance overhead
because of functions like PatternCount, I'd like to make the argument
that #Assign is a convenience function.

If I (or any other developer) is going to be composing a script for the
purpose of the best possible performance, then obviously using straight
positional parameters in order to bypass the overhead of calling custom
functions is going to be the preferred solution.

My primary reason for adding this code is for the sake of convenience
when using ExecuteSQL() in order to return Let formatted results which
can be evaluated using #Assign.

Could I inject the semi's within the SQL. Yes, are there other places
and times when I compose a Let dictionary without using #()? Yes.

Does this seems like I'm being a lazy programmer? Maybe.

Did I write any new tests? No. What tests do you want to see?

Is it really necessary to account for unbalanced semicolon endings?
Overkill.
Do I need to account for endings with "; *¶" (regex)?
Further testing overkill.

Let me know what you think.

Matt

Matt Petrowsky

unread,
Nov 19, 2015, 2:18:28 AM11/19/15
to FileMaker Development Standards

Jeremy Bante

unread,
Nov 20, 2015, 8:26:27 PM11/20/15
to FileMaker Development Standards
TL;DR: Performance really is a good reason to use the #Assign function, but your addition to the #Assign function doesn't appreciably hurt it's performance relative to other techniques for passing multiple script parameters.

On Wednesday, November 18, 2015 at 10:57:42 PM UTC-8, Matt Petrowsky wrote:
Before you throw out the argument of this adding performance overhead
because of functions like PatternCount, I'd like to make the argument
that #Assign is a convenience function.

If I (or any other developer) is going to be composing a script for the
purpose of the best possible performance, then obviously using straight
positional parameters in order to bypass the overhead of calling custom
functions is going to be the preferred solution.

It's neither obvious nor always true that positional (e.g. return-delimited) parameters are faster to parse than Let Notation. For reasons described and tested on a recent fmforums thread, basic parsing of positional values requires quadratic (O(N^2)) time. As I pointed out in this thread, so does every other non-#Assign method. At best, positional parameters can be improved to linearithmic (O(N*log(N))) performance, which does not pay off until we get to larger lists than I've ever passed as a script parameter. As an intermediate step, positional parameters can be improved to O(N^1.5) performance, which can pay off with substantially smaller data. The approach taken by the #Assign function is linear (O(N)), as best as I can tell. Asymptotically, positional parameters aren't even close. I don't think I've ever worked in a FileMaker solution where script parameters ranked as a substantial performance bottleneck compared to other factors; but the larger the parameter is, the more plausible that is, and it's exactly in situations with large parameters where positional parameters get left in the dust. That's not to say we don't pick the low-hanging fruit when we can help it, even when it isn't the sweetest fruit on the tree...

For small numbers of parameters, the comparison can be different. It's sometimes possible for asymptotically worse algorithms to perform better up to a certain crossover point. It's rarely obvious what that crossover point might be. (Your edit to the #Assign function doesn't do anything to change the asymptotic performance class, but it might shift the crossover point.) So I tested. Comparing scripts that accept an arbitrary number of parameters using return-delimited list parsing vs. custom function-free parsing that requires strictly well formatted Let Notation, the Let Notation is no slower (sometimes even faster!), even for only one UUID-sized parameter. At only 1 parameter, this isn't exactly a representative comparison, but it does demonstrate that Let Notation is in fact pretty damn fast. Past that point with more parameters, Let Notation, whether parsed without a custom function, or with the old or new variations on the #Assign function, are consistently faster than parsing return-delimited lists, and by a healthy margin, and even with very few parameters consistent with what we see in most FileMaker scripting. Consistent with expectations, the more elaborate custom functions are a little slower than the more basic parsing without a custom function, and the new custom function is very slightly slower than the less tolerant older one.

Jeremy Bante

unread,
Nov 20, 2015, 8:27:21 PM11/20/15
to FileMaker Development Standards
I forgot to attach the test file. Oops.
Screen Shot 2015-11-20 at 17.14.17.png
Screen Shot 2015-11-20 at 17.18.52.png
ListPerformanceComparison.fmp12.zip

Matt Petrowsky

unread,
Nov 20, 2015, 8:54:33 PM11/20/15
to FileMaker Development Standards
Well, then. That seems to be settled. :)
Reply all
Reply to author
Forward
0 new messages