Lack of the local function abstraction is IMO the single most important factor which leads to unmaintainable FlexSim code. In the wild, I routinely see nodefunctions with over 1000 lines or code, 10+ levels of code nesting, and cyclomatic complexity through the roof (100+).
FlexSim already supports fsnodes and nodefunctions, which are text nodes with chunks of code with two different parameter-passing conventions, and usercommands, which are basically the same thing + metadata. Usercommands are the closest thing to "functions" in other languages.
The problems with relying only on usercommands are:
- usercommands pollute the global namespace and are suggested by autocomplete everywhere
- usercommands are rather unwieldy to create¹
¹ Creating a "nodefunction" in MAIN:/project/exec/usercommandlist takes approximately 20 mouse clicks (!), a couple of keyboard shortcuts, not counting writing the function itself and all the boilerplate.
There are also "applicationcommands", which is a fancy way to hide nodefunctions from the user and exclude them from autocomplete. The problems with applicationcommands are essentially the same:
- they share the same global namespace
- they're only slightly easier to create
- they're not supported by function-call syntax, but should be called by name through
applicationcommand()
.
Eventually, users prefer copy-pasting their code rather than going through the hoops to define nodefunctions in FlexSim way. It should be possible to write something like this in any nodefunction:
var localFunction = function(type varname, …) { /* use varname immediately */ } // ... localFunction(somevalue); // ...
Technically, it is already possible to craft "local functions". FlexSim supports at least three kinds of function-like objects: to be called by executefsnode()/applicationcommand(), nodefunction()/applicationcommand(), and as user commands respectively. All these objects can be constructed and destroyed at runtime. See lambda() hack below.
This solution is far from elegant (lots of boilerplate, no syntax highlighting support, necessity to manually escape strings, no autocomplete), but it works. FlexScript is only one step away from having a normal way to define functions.
The proposal is to allow this syntax (or similar):
var mycmd = function() { for (int i = 1; i <= 3; i++) { pt("HELLO FLEXSCRIPT WORLD\n"); } }; mycmd();
P.S. lambda() hack
Please find attached a user command lambda(fn) and its companion purgelambda([int]), where fn can be either an existing nodefunction, or a string with the function code. Positional arguments can be referred to as %1, %2, ...:
flexsim-functools.lambda.fsx
flexsim-functools.purgelambda.fsx
You may also get code snippets from
Usage:
var mySumFn = lambda("return someflexsimcommand(%1 + %2);"); passFunctionAsParameter(mySumFn); purgelambda();
lambda() is a hack, but it's a necessary evil because Flexscript functions ("commands") are not first class and cannot be passed as values. While code chunks ("nodefunctions") are first class, they cannot be invoked the same way as commands.
Something like this should really be part of the platform. It should be possible to define functions in-code. It should be possible to define function with a limited scope of visibility. Invocation syntax for commands and nodefunctions should be unified. It should be possible to pass user commands as arguments to other functions.