Returning an expression from a function differs when namespace is used.
Jul 16, 2023 09:34 PM(3728 views)
Hello everyone,
I ran into some strange behavior when returning an expression from a function, and could not find any discussions about this already. I am in the habit of using namespaces for pretty much everything. I found that when a function returns an expression, the expression is not evaluated for an unscoped function, but is evaluated if a namespace is used. Does anyone know what's going on here?
This script should show that the variable x is not incremented when the first function is called, but is incremented on the second function call. I am using JMP v17.0.0, so this has possibly been fixed, but I am not able to update right now.
The two issues are probably related. Items specified inside the namespace function call are not constructed until they are used (they are kept as expressions.) That means f1 is not compiled when the namespace is created, but f1 is compiled each time it is used, thus slower. So there is probably some specialized C++ code to recompile f1 and then evaluate it. My guess is that same code is triggered for f2, but doesn't recompile because f2 is already compiled, and then incorrectly evaluates the result of f2, which most of the time is a primitive value and no one notices. The expr() value that is returned does evaluate into a simpler value, causing you to notice.
The two issues are probably related. Items specified inside the namespace function call are not constructed until they are used (they are kept as expressions.) That means f1 is not compiled when the namespace is created, but f1 is compiled each time it is used, thus slower. So there is probably some specialized C++ code to recompile f1 and then evaluate it. My guess is that same code is triggered for f2, but doesn't recompile because f2 is already compiled, and then incorrectly evaluates the result of f2, which most of the time is a primitive value and no one notices. The expr() value that is returned does evaluate into a simpler value, causing you to notice.
If anyone else encounters this problem the quick workaround is as the example suggest; Wrap the return value in "Name Expr" until the desired result
This does not work. I had a x defined in the global/here scope from previous runs. There seem to be no way to return an expression from a function defined like ns:f2
This definitely seem like a bug, i dont think anyone would expect a ns:f2 to return a different value compared to ns:f1 or f1; the function code is exactly the same.
That solution is indeed more elegant, however it seem to only work when you can directly write the expression to return.
If the expression is stored in a variable and we wrap that in an Expr() the value returned is an expression with the variable name
In this case it seems the only solution seems to be to wrap the return value in a Name Expr() and the entire function in an expr
funcNS:f5 = Expr(Function( {}, {Default Local},
x = Expr(1+2);
Name Expr(x)
));
Show( funcNs:f5() ); // 1+2
One big danger with this us that the second evaluation seem to happen outside the local scope of the function.
A global variable might shadow the return value, which would not give an error.
Names Default to Here(1);
Delete Symbols(); // Only use if you dont have variables you need
funcNS = New Namespace("funcNS");
funcNS:f1 = Function( {}, {Default Local},
x = Expr(1+2);
Expr(x);
);
funcNS:f2 = Function( {}, {Default Local},
x = Expr(1+2);
Expr(Expr(x));
);
funcNS:f3 = Expr(Function( {}, {Default Local},
x = Expr(1+2);
x;
));
funcNS:f4 = Expr(Function( {}, {Default Local},
x = Expr(1+2);
Expr(x);
));
funcNS:f5 = Expr(Function( {}, {Default Local},
x = Expr(1+2);
Name Expr(x)
));
Show( Try(funcNs:f1(), exception_msg) ); // Name Unresolved
Show(funcNs:f2()); // X
Show(funcNs:f3()); // 3
Show(funcNs:f4()); // x
Show(funcNs:f5()); // 1+2
// Second Evaluation happens outside Function scope
x = Expr(Expr(1+10));
Show(funcNs:f1()); // 1+10