cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Discussions

Solve problems, and share tips and tricks with other JMP users.
Choose Language Hide Translation Bar
AlterEgo
Level III

How to manipulate JSL function arguments

I'm leaving this here if it helps someone. Maybe I'm inventing hot water, but I couldn't find anything similar.

Problem: in general it is not possible to directly access the parts of a function "expression" directly, expression in quotes because it's not really an Expression data type but a Function, thus the usual expression manipulation functions do not work:

f = Function( { x }, Return( x ) );

Show( Type( Name Expr(f) ), N Arg( Name Expr(f) ) );

// output:
Type(Name Expr(f)) = "Function";
N Arg(Name Expr(f)) = 0;

However, there's a trick - you can transform a function to an Expression data  type using:

Parse( Char( Name Expr( function ) ) )

Maybe there's a more elegant way but after hours of research and my own testing I could not find it, and even this method I found pretty much by coincidence just before giving up.

Anyway, then you can access its arguments using Arg(). It has 2 or 3, depending on presence of locals list. First expression argument is the function inputs list, the second one is locals list if present, the last one is function body.

f = Function( { greeting, planet }, { output },
	output = Concat( greeting, " ", planet, "!");
	Return( output )
);

f_as_expr = Parse( Char( Name Expr( f ) ) );

Show( Arg( f_as_expr, 1 ), Arg( f_as_expr, 2 ), Arg( f_as_expr, 3 ) )

// output:
Arg(f_as_expr, 1) = {greeting, planet};
Arg(f_as_expr, 2) = {output};
Arg(f_as_expr, 3) = output = greeting || " " || planet || "!"; Return(output);

For example, I have a user interface function  with lots of display boxes and most of them are assigned to a variable. I update it quite often so after a while maintaining the locals list becomes painful since I don't want to use {default local} because of less control over variable scope and possible leakage. I know I could use Associative array or similar approach, but since I also maintain the list of display box names in an external list for other purposes, I can dynamically inject it into the function as locals. Of course, you can also modify the function inputs and body in a similar way.

Names Default To Here(1);

// f must be passed as Name Expr(f), add_locals as list of strings
inject_locals = Function( { f, add_locals }, { f_as_expr, cur_locals, new_locals },
	f_as_expr = Parse( Char( Name Expr( f ) ) );
	add_locals = Transform Each( {al}, add_locals, Parse(al) );
	Match( N Arg( f_as_expr ),
		2,
		Insert Into( f_as_expr, add_locals, 2 ),
		3,
		cur_locals = Arg( f_as_expr, 2);
		new_locals = cur_locals || add_locals;
		Substitute Into( f_as_expr, cur_locals, new_locals ),
		Throw( "inject_local error: unexpected function structure.")
	);
	Return( Eval( Name Expr( f_as_expr ) ) )
);

// simplified example of use
f = Function( {}, { output },
	greeting = "Hello";
	planet = "Earth";
	output = Concat( greeting, " ", planet, "!" )
);

// globals to show that f internals remain local after injection
greeting = "Goodbye";
planet = "Mars";

additional_locals = { "greeting", "planet" };

f_injected = inject_locals( Name Expr(f), additional_locals );

Show( f, f_injected, f_injected(), greeting, planet )

// output:
f = Function({},{output},greeting = "Hello"; planet = "Earth"; output = greeting || " " || planet || "!");

f_injected = Function({},{output, greeting, planet},greeting = "Hello"; planet = "Earth"; output = greeting || " " || planet || "!");

f_injected() = "Hello Earth!";
greeting = "Goodbye";
planet = "Mars";
0 REPLIES 0

Recommended Articles