cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Browse apps to extend the software in the new JMP Marketplace
Choose Language Hide Translation Bar
bculver
Level II

Returning an expression from a function differs when namespace is used.

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.

 

NewNamespace("varNamespace");
varNamespace:x = 0;
myFunc = Function({}, {DEFAULTLOCAL}, returnVal = Expr(varNamespace:x++); NameExpr(returnVal); ); myFunc(); Show(varNamespace:x); NewNamespace("functionTest"); functionTest:myFunc = Function({}, {DEFAULTLOCAL}, returnVal = Expr(varNamespace:x++); NameExpr(returnVal); ); functionTest:myFunc(); show(varNamespace:x);
1 ACCEPTED SOLUTION

Accepted Solutions
Craige_Hales
Super User

Re: Returning an expression from a function differs when namespace is used.

Pretty sure you've discovered a bug. Here's a even more simplified demo of two ways to use functions in namespaces:

funcNS = New Namespace(
	"funcNS",
	f1 = Function( {}, 
		expr(1+2)
	)
);

funcNS:f2 = Function( {}, 
	expr(1+2)
);
	
Show( Type( funcNS:f1() ), Type( funcNS:f2() ) );

Type(funcNS:f1()) = "Expression";
Type(funcNS:f2()) = "Number";

 

f1 is returning the correct answer; f2 is incorrectly evaluating the answer. I prefer the f2 way (except for this bug) because it is faster:


funcNS = New Namespace(
	"funcNS",
	f1 = Function( {}, 
		1
	)
);

funcNS:f2 = Function( {}, 
	1
);
	
nf1 = 0;
For( endtime = HP Time() + 1e6, endtime > HP Time(), nf1++,
	funcNS:f1()
);
Show( nf1 ); // 180K calls
nf2 = 0;
For( endtime = HP Time() + 1e6, endtime > HP Time(), nf2++,
	funcNS:f2()
);
Show( nf2 ); // 260K calls

nf1 = 182239;
nf2 = 258929;

 

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.

 

View more...
code I played with to check my understanding
c=1; d="hi"; e=["key1"=>"val1"];
a = New Namespace( "a",
	w = {c, d, e},
	x = Eval List( {c, d, e} ),
	 // the RHS expression is assigned to x
	y = 1 // this does what you expect because 1 == expr(1)
);
Show( a );
/* The namespace is holding the expressions...
a = New Namespace("a", {
w = {c, d, e},
x = Eval List( {c, d, e} ),
y = 1
});
*/
Show( a:x ); // a:x = {1, "hi", ["key1" => "val1"]};
/////////// change some globals
c=2; d="there"; e["key2"]="newval";
Show( a:x ); // a:x = {2, "there", ["key1" => "val1", "key2" => "newval"]};

a:z = Eval List( {c, d, e} );
show(a);
/*
a = New Namespace("a", {
w = {c, d, e},
x = Eval List( {c, d, e} ),
y = 1,
z = {2, "there", ["key1" => "val1", "key2" => "newval"]}
});
*/

@julian  - pretty sure this is a bug. I don't think there will be many namespace users depending on the wrong behavior.

Craige

View solution in original post

2 REPLIES 2
bculver
Level II

Re: Returning an expression from a function differs when namespace is used.

I have updated to 17.2.0, and can confirm this is still issue still occurs.

 

Here is a simplified example that should hopefully be easier to read

x = 0;
myFunc = Function({}, {DEFAULTLOCAL},
	Expr(::x++);
);

myFunc(); // Returns unevaluated expression: Expr(::x++)
Show(::x); // ::x=0;

NewNamespace("funcNS");
funcNS:myFunc = Function({},{DEFAULTLOCAL},
	Expr(::x++);
);

funcNS:myFunc(); // Returns evaluated expression;
show(::x); // ::x=1;
Craige_Hales
Super User

Re: Returning an expression from a function differs when namespace is used.

Pretty sure you've discovered a bug. Here's a even more simplified demo of two ways to use functions in namespaces:

funcNS = New Namespace(
	"funcNS",
	f1 = Function( {}, 
		expr(1+2)
	)
);

funcNS:f2 = Function( {}, 
	expr(1+2)
);
	
Show( Type( funcNS:f1() ), Type( funcNS:f2() ) );

Type(funcNS:f1()) = "Expression";
Type(funcNS:f2()) = "Number";

 

f1 is returning the correct answer; f2 is incorrectly evaluating the answer. I prefer the f2 way (except for this bug) because it is faster:


funcNS = New Namespace(
	"funcNS",
	f1 = Function( {}, 
		1
	)
);

funcNS:f2 = Function( {}, 
	1
);
	
nf1 = 0;
For( endtime = HP Time() + 1e6, endtime > HP Time(), nf1++,
	funcNS:f1()
);
Show( nf1 ); // 180K calls
nf2 = 0;
For( endtime = HP Time() + 1e6, endtime > HP Time(), nf2++,
	funcNS:f2()
);
Show( nf2 ); // 260K calls

nf1 = 182239;
nf2 = 258929;

 

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.

 

View more...
code I played with to check my understanding
c=1; d="hi"; e=["key1"=>"val1"];
a = New Namespace( "a",
	w = {c, d, e},
	x = Eval List( {c, d, e} ),
	 // the RHS expression is assigned to x
	y = 1 // this does what you expect because 1 == expr(1)
);
Show( a );
/* The namespace is holding the expressions...
a = New Namespace("a", {
w = {c, d, e},
x = Eval List( {c, d, e} ),
y = 1
});
*/
Show( a:x ); // a:x = {1, "hi", ["key1" => "val1"]};
/////////// change some globals
c=2; d="there"; e["key2"]="newval";
Show( a:x ); // a:x = {2, "there", ["key1" => "val1", "key2" => "newval"]};

a:z = Eval List( {c, d, e} );
show(a);
/*
a = New Namespace("a", {
w = {c, d, e},
x = Eval List( {c, d, e} ),
y = 1,
z = {2, "there", ["key1" => "val1", "key2" => "newval"]}
});
*/

@julian  - pretty sure this is a bug. I don't think there will be many namespace users depending on the wrong behavior.

Craige