Optional Arguments not behaving as expected

I notice that when I run the code in Code Sample 1, that after the first iteration the value of the optional argument ‘data’ is not the set default of {cl={}, ch={}}. Instead it appears to be the value of ‘data’ from the previous iteration, with its sub table(s) retaining their contents. Why does this occur and is it intended?

For comparison Code Sample 2 is the modification I had to make to make it perform as I was expecting it too.

Code Sample 1:
`function loadTable(callback, data={cl={}, ch={}}){
server.log("data = "+http.jsonencode(data));

data.cl.n <- 127;
data.ch.g <- 303;

imp.wakeup( 1, function(){
    callback(data);
});

}

function runTest(){
loadTable(function(cols){
runTest();
});
}

runTest();`

Code Output 1:
2017-01-17 19:04:49 UTC+13 [Agent] data = { “cl”: { }, “ch”: { } }
2017-01-17 19:04:50 UTC+13 [Agent] data = { “cl”: { “n”: 127 }, “ch”: { “g”: 303 } }
2017-01-17 19:04:51 UTC+13 [Agent] data = { “cl”: { “n”: 127 }, “ch”: { “g”: 303 } }
2017-01-17 19:04:52 UTC+13 [Agent] data = { “cl”: { “n”: 127 }, “ch”: { “g”: 303 }

Code Sample 2:
`function loadTable(callback, data=null){
if(data==null)
data = {cl={}, ch={}};
server.log("data = "+http.jsonencode(data));

data.cl.n <- 127;
data.ch.g <- 303;

imp.wakeup( 1, function(){
    callback(data);
});

}

function runTest(){
loadTable(function(cols){
runTest();
});
}

runTest();`

Code Output 2:
2017-01-17 19:01:52 UTC+13 [Agent] data = { “cl”: { }, “ch”: { } }
2017-01-17 19:01:53 UTC+13 [Agent] data = { “cl”: { }, “ch”: { } }
2017-01-17 19:01:54 UTC+13 [Agent] data = { “cl”: { }, “ch”: { } }
2017-01-17 19:01:55 UTC+13 [Agent] data = { “cl”: { }, “ch”: { } }

This is more a @peter question, I suspect it’s to do with default parameters being essentially static (ie it’s a reference to the table, not a local).

Your second example makes it a local.

That makes sense. Thanks for the response.

As Hugo says, this is because the expression given for a default parameter is evaluated once (at startup, in fact), and the same value is assigned to the parameter every time it’s needed. This means that if the value of the expression is of a reference type – such as the table in your example – a reference to the same table is passed every time. Sadly this means that default parameters are only really useful for scalar types – ints, floats and strings – though other languages (C++, JavaScript) do also have this behaviour.

The code in your Sample 2 is indeed the best way of getting the behaviour you want.

Peter

@peter, that’s quite significant to know that. I have been partial, in many cases, to assign an empty table as a default parameter. I had thought it would only be used when and only when the function is called. If they are evaluated at startup, it’s costing me a couple of K in wasted space.

Interesting. I’ve been bitten by this too, but never got round to asking why. I’ll update the Squirrel Programming Guide accordingly

Question: it seems that the ‘data’ table in the above example is not static, but that it’s 'data’s reference type properties that are. Change the server.log() line to

`
server.log("data = "+http.jsonencode(data) + " DataRef: " + data + " clRef: " + data.cl);`

and the DataRef value changes with each pass through the function:

`
[agent.log]    data = { "cl": { }, "ch": { } } DataRef: (table : 0x7f4157184cd0) clRef: (table : 0x7f41c6977b60)
[agent.log]    data = { "cl": { "n": 127 }, "ch": { "g": 303 } } DataRef: (table : 0x7f4154bd7c10) clRef: (table : 0x7f41c6977b60)
[agent.log]    data = { "cl": { "n": 127 }, "ch": { "g": 303 } } DataRef: (table : 0x7f42097b6e80) clRef: (table : 0x7f41c6977b60)
[agent.log]    data = { "cl": { "n": 127 }, "ch": { "g": 303 } } DataRef: (table : 0x7f41f87fcbe0) clRef: (table : 0x7f41c6977b60)`

So, why does Squirrel so it this way? Why not make ‘data’ static too?

That’s the way it is, and we have to work accordingly, but I’d be interested to know why is it the way it is. Is there a rationale to this behaviour?

(or is there something else going on?)