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";