Regarding functions (both in-built and user-defined) in Gekko 3.0, these implement so-called UFCS (Uniform Function Call Syntax). First, some background.
In object-oriented languages, a method has the form x.f(a, b), where x is the object, and f() is the method, with arguments a and b (this is also called a member function). In contrast, a function has the form f(a, b), where a and b are again the arguments (this is also called a free function). When designing the syntax for Gekko 3.0, the distinction between methods and functions came up. For instance, consider a function returning the length of a Gekko list. If the list is called #m, we could use either #m.length() or length(#m). Both make sense, and both have their advantages and drawbacks. Gekko 2.0 and earlier only had free functions, but it became clear that objects methods were needed, in order to implement method chaining and fluent interfaces so that we can write, for instance, #m = #m1.append(‘a’).except(#m2).sort(). This means taking the list #m1, adding the string ‘a’, removing elements from the list #m2, and finally sorting the list alphabetically. Using free functions, this would read like #m = sort(except(append(#m1, ‘a’), #m2)), which is much harder (non-fluent) to read, because it reads backwards and from the inside out.
So regarding Gekko 3.0 there was no doubt that object methods had to be implemented. But there was a really nagging question. In some cases where objects are involved, a free function also makes sense, and choosing between implementing for instance the length of a list as an object method or free function seemed arbitrary. This is also seen in computer languages like C#, Java, Python and others, where it is not always obvious whether some functionality is implemented as an object method or a free function. For instance, in Python, the length of a list is len(m), whereas in C# it would be m.Count(). So for Gekko 3.0, choosing between length(#m) and #m.length() was not obvious, and contemplating whether a given functionality should be an object method or free function was both time-consuming and a bit frustrating. But then the author discovered the UFCS idea, which is used in, for instance, the D language. The idea is really simple: a given free function f(a, b) can always be written in the alternative form a.f(b), where the first argument of f(a, b) is moved outside the parentheses, and then followed by a dot. This is purely syntactic sugar: when Gekko 3.0 sees a function like a.f(b), it is first transformed into a “normal” free function f(a, b), and then executed. So when implementing funtions in Gekko 3.0, if the user is careful about putting the “object” (or primary argument) first in the argument list, calling the function like an object method becomes possible. Therefore, Gekko users can also design user-defined functions that can be called with an object-oriented flavor.
Consider a user-defined function like rescale(x, %v) which rescales the values of the timeseries x with the scaling factor %v. Because the timeseries is the first argument, this can alternatively be written as x.rescale(%v), with a more object-oriented feeling. All in all, there are many advantages regarding UFCS, and this author wonders why it is not standard in all computer languages. Bjarne Stoustrup (the father of C++) has proposed to implement UFCS in the C++ language too, so the idea probably has no obvious drawbacks.
Another question regarding functions in Gekko 3.0 is the use of optional time parameters in functions. For instance, the above-mentioned rescale(x, %v) function could allow a time period, for instance rescale(x, %t1, %t2, %v), where %t1 and %t2 represent the time period — which could be written x.rescale(%t1, %t2, %v), too. But the function should also be callable without time paramaters, in which case the enclosing time period could be used (either the local period in a Gekko command, or the global Gekko time period). Since periods are fundamental in Gekko, functions in Gekko 3.0 allow an alternative (and better) way of stating time periods: rescale(<%t1 %t2>, x, %v), or written with UFCS: x.rescale(<%t1 %t2>, %v). Note here that the Gekko UFCS implementation counts the first argument as the first argument that is not a <>-period. This special treatment of <>-periods is convenient, because in that way, the periods are always written as the first argument, like Gekko commands. But this is not the end of the conveniences. When defining a user-defined function that can be called with, for instance, x.rescale(<%t1 %t2>, %v), the %t1 and %t2 time parameters are obvisously used inside the function (why else use them as arguments?). But what if the rescale() function is defined with time parameters, but called without them, like x.rescale(%v)? In that case, Gekko will hook the time parameters inside the function to the enclosing time period. This is similar to a command like PRT <2020 2025> x, which uses the period 2020-25, whereas PRT x uses the global time period. One nice thing about optional time parameters is that they can always later on be added to a function, without breaking existing code.