Suppose I have a function, e.g.
f[x_] := x^2
It works like this:
f[5]
(* 25 *)
I would like to permanently modify it so that calling f[5] will perform some action before and after the evaluation of the original f. For example, I would like f to behave like ff below:
ff[x_] :=
Module[{res},
Print["Before..."];
res = f[x];
Print["After..."];
res
]
ff[5]
During evaluation of In[]:= Before...
During evaluation of In[]:= After...
(* 25 *)
I would like to do this without needing to know what f does, or how it behaves. Preferably, the modification should be carried out automatically. This would be somewhat similar to decorators in Python.
The Villegas–Gayley pattern makes this feasible for built-in functions because built-in definitions always have lower priority than user-issued ones. But with user-defined functions this is much harder to do reliably because we cannot control the ordering of definitions.
Example application:
Evaluate functions with a certain value of $ContextPath. Ideally, all the functions in the package described in the linked question could be modified in one line of code.
If we have
sym;
g[] := ToString[sym]
then
g[]
(* "sym" *)
Suppose we want to automatically modify g[] so that it behaves like
Block[{$ContextPath = {}, $Context = "myEmpty`"}, g[]]
(* "Global`sym" *)
Answer
Based on your requirements I would move the original symbol to a new context.
wrapper[fn_, mod_] :=
Module[{ctxt = Context[fn], name = SymbolName[fn], x},
Context[fn] = "wrapper`" <> ctxt <> ToString[x] <> "`";
Symbol[ctxt <> name][arg___] := fn[arg] // Unevaluated // mod
]
Your example:
sym;
g[] := ToString[sym]
wrapper[g, Block[{$ContextPath = {}, $Context = "myEmpty`"}, #] &]
g[]
"Global`sym"
Recursion:
fib[1 | 2] = 1;
fib[n_Integer?Positive] := fib[n - 1] + fib[n - 2]
wrapper[fib, #2 &[Print["Before"], #, Print["After"]] &]
fib[7]
Before
After
13
Nested wrappers:
wrapper[fib, #2 &[Print["Primordial"], #, Print["Final"]] &]
fib[12]
Primordial
Before
After
Final
144
Related:
Comments
Post a Comment