In official article, TradingView explains that if you don't call function on every bar, it will return inconsistent values, because it isn't called on every bar.
In the article, it is said that if
foo(a) => a[1]
isn't called every bar, but every other bar, it returns value two bars ago, because it returns last value, which was recorded 2 bars ago when function was called...
However, this makes completely no sense, when I tried the following code:
foo(c) => c[3]
bar() => close[3]
plot(open[3], color=color.blue)
plot(bar(),color=color.green)
plot(close[3],color=color.red)
plot(foo(open), color=color.aqua)
plot(foo(close),color=color.lime)
This code produces two same lines. So, close[3], function returning close[3], and function returning [3] on its variable all display the same thing.
But how could that be? If it would return value from 3 executions ago, wouldn't it be like this?
foo(open) called on last bar
foo(close) called on last bar
foo(open) called on current bar
foo(close) called on current bar
so it should return [3], meaning, it should return the value it had when it was called on the last bar, with open of the last bar? So it should return open value of the last bar, no?
If not, then how does it distinguish when it is called with different arguments? And if it doesn't and it does correctly go into history of the variable passed to it (correctly going into history of open and close when provided with that), that means the article is wrong, and there should be no inconsistency?
EDIT: Additional example
foo(c) => c[7]
everyOtherBar = bar_index % 3
plot(high[7]+1, color=color.blue)
plot(foo(everyOtherBar == 2 ? high+1 : everyOtherBar ? hl2+1 : low+1), color=color.lime)
plot(low[7]+1,color=color.red)
So I have made another test. I feed different values to f(a) and it remembers them all properly. The outcome is as expected, without errors. So we must assume when f(a) gets called, it gets WHOLE timeline? Because the function didn't get the argument every bar, like, it doesn't know what's high value 7 bars ago because it didn't see high every bar, so how could it?
By the way, added +1 so it couldn't, like, remember the variable name, because now there's no variable to remember, it's an equation... but still, it works? So it stores the results of all past values of EVERYTHING? And all that, shared with every local function? Like, once f(a) got called this bar, it not only gets current value fed to it, but like, EVERYTHING? Because I can somehow know past values of close+1 even though it only saw the result of open+1?
So supposedly, my global space has 10mb of data, and I call 20 functions from that, I would create extra 200mb of data just then?
EDIT: PS: In case anyone comes up on this question, I figured out the solution is that indeed, every function call in the code is a completely isolated local space for that function, that has its own history. I don't believe it's ever explained anywhere else
This code demonstrates it.
//@version=5
OVERLAY = false
indicator("Pastebin", overlay = OVERLAY)
f(a, inc = 1) =>
b = a
var called = 0
called := called + inc
var accumulator = 0.
accumulator += a
[a[2], called, accumulator]
[fc, called1, _void1] = f(close)
[_void2, called2, accumulator1] = f(open)
[fo, called3, accumulator2] = f(open, 2)
plot(close[2] - fc)
plot(open[2] - fo)
plot(called1)
plot(called2)
plot(called3)
plot(accumulator1 - accumulator2)
Even though function f(a) is called three times, the amount times called is individually stored. If that would not be so, we would see "called" value increase within one price candle from call to call. Also, an variable "accumulator" incremented inside the function itself is isolated, meaning, it stores individual values for individual functions, therefore, between two function calls with same input, it has same output, since each input went into its own "accumulator" value.
So one must assume that every function call inside global code (and from functions) creates its own local space where history is stored. So yes calling function 20 times would produce 20 copies of history, and each copy would individually operate.
This also means one can use local function vars without fear of them being contaminated by multiple function calls. But must expect them to NOT be influenced by multiple function calls. For example, if I would want to have a counter of how many times a particular function was called, in total, I'd have to use an array, otherwise each function call would only calculate times that specific call was executed.
And that's why calling functions every tick is important if they do [] inside, because they would have desynced [] values with the rest of the global space if they weren't. That's also why it's not enough to call a function once for it to count, meaning something like
foo(a) => a[1]
foo(close)
if(close > open)
foo(open)
Would give a warning, since second function call isn't getting complete history, since it's an isolated local space
Again, if you would want to track history inside a function that does implement this as a feature without warning, you would have a function that starts with if(condition) and whole function is within that if block, and within it, you use an array you unshift an item into every time the function is ran. This way, array.get/set on this array will give you an equivalent of [] for same index value (0 = now, 2 = two executions back etc)