Scoping goes like this -- lookup in Local()
namespace if it exists, if not lookup in Here()
namespace if it exists, if not lookup in Global()
namespace. (Also, it will check the column names of the current data table sometimes as well).
What you're doing in your first script is to put the name "x"n
into the Here()
namespace with value "Variable"
, then put the name "exprA"n
into the Here()
namespace with value Write( "Here is variable: " || x )
, then put name "exprB"n
into the Here()
namespace with value Glue( dt1 = Open( "$SAMPLE_DATA/Big Class.jmp" ), dt1 << On Close( exprA ) )
.
Finally you evaluate "exprB"n
-- JMP then tries to resolve this name by first checking the Local()
namespace (it doesn't exist), then the Here()
namespace and it finds it -- so it runs the expression stored in As Scoped( "here", "exprB" )
.
This then loads the table and puts exprA
as the closing script to the window.
Since this is the end of the script then the Here()
namespace created for the script is deleted and the memory cleared.
When you close the table, it finds the literal exprA
as the closing script, then it tries to scope the name as above (in Local()
, Here()
, Global()
), only this time there is no Here()
namespace and it cannot find the name in any of the three namespaces, thus throwing an error.
In general, when you want to add a variable as a static value to an expression that will run in a different scope, use either Eval( Eval Expr( ...; Expr( Name Expr( val ) ); ... ) )
or some other equivalent.
In my personal experience I create explicit namespaces for each script (anonymous) then manually scope all variables to the respective namespace. I can then pass this namespace around as needed (and of course I clear and close the namespace when the script is done / window is closed).
Some examples of how your script could be made to work are as follows:
Names Default To Here( 1 );
x = "Variable";
Eval( Eval Expr(
dt1 = Open( "$SAMPLE_DATA/Big Class.jmp" );
dt1 << OnClose( Write( "\!NHere is variable: " || Expr( x ) ) );
) );
or
Names Default To Here( 1 );
x = "Variable";
exprA = Expr(
Write( "\!NHere is variable: " || x )
);
Eval( Eval Expr(
dt1 = Open( "$SAMPLE_DATA/Big Class.jmp" );
dt1 << OnClose( x = "rather"; Expr( Name Expr( exprA ) ) );
) );
or
Names Default To Here( 1 );
self = New Namespace();
self:x = "Variable";
self:exprA = Expr(
Write( "\!NHere is variable: " || self:x )
);
Eval( Eval Expr(
dt1 = Open( "$SAMPLE_DATA/Big Class.jmp" );
dt1 << OnClose(
Local( {self = Namespace( Expr( self << Get Name ) )},
self:exprA;
Delete Namespaces( Force( 1 ), self )
)
);
) );
Note that while many programming languages create local namespaces and keep them around using reference counters until they're no longer needed (such as Python with functions and such), JSL doesn't really do that and it is up to the coder to maintain any persistence and scoping needed. The code inside the <<On Close( ... )
message is evaluated in a completely separate context to the script that created it with no management of names or scopes.
Hope this helps, it can be a bit confusing!!
Jordan