Dejo esto aquí por si le sirve a alguien. Quizás estoy inventando algo parecido, pero no he encontrado nada similar.
Problema: en general, no es posible acceder directamente a las partes de una función "expresión" (expresión entre comillas porque en realidad no es un tipo de dato Expresión sino una Función), por lo que las funciones habituales de manipulación de expresiones no funcionan:
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;
Sin embargo, hay un truco: puedes transformar una función a un tipo de datos Expression usando:
Parse( Char( Name Expr( function ) ) )
Quizás exista una forma más elegante, pero después de horas de investigación y de mis propias pruebas no pude encontrarla, e incluso este método lo descubrí casi por casualidad justo antes de darme por vencido.
En cualquier caso, puedes acceder a sus argumentos usando Arg(). Tiene 2 o 3, dependiendo de si existe una lista de variables locales. El primer argumento de la expresión es la lista de entradas de la función, el segundo es la lista de variables locales si está presente, y el último es el cuerpo de la función.
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);
Por ejemplo, tengo una función de interfaz de usuario con muchos cuadros de visualización, la mayoría de los cuales están asignados a una variable. La actualizo con bastante frecuencia, por lo que mantener la lista de variables locales se vuelve complicado, ya que no quiero usar `{default local}` debido a la menor capacidad de control sobre el alcance de la variable y las posibles fugas de memoria. Sé que podría usar un array asociativo o un enfoque similar, pero como también mantengo la lista de nombres de cuadros de visualización en una lista externa para otros fines, puedo inyectarla dinámicamente en la función como variables locales. Por supuesto, también se pueden modificar las entradas y el cuerpo de la función de forma similar.
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";