6
$\begingroup$

Please, consider these functions:

f[x_] := Module[{},
   Return[x + 1];
   ];
g[x_] := Module[{},
   Return[x - 1];
   ];

I want evaluate doSomething each time f (or g) is invoked. The obvious way

f[x_] := Module[{},
   doSomething;
   Return[x + 1];
   ];
g[x_] := Module[{},
   doSomething;
   Return[x - 1];
   ];

presents a serious drawback, because it delivers on the programmer's shoulders the responsibility to add the required command inside each function. Little it may seems, in large projects it's a major source of (my...) , hard to trace back, oversights. Leaving aside metaprogramming, is it possible escape from this boilerplate code by means of a function whenInvoked[{f,g,...} , doSomething] ?

Addendum

I'm not "looking to alter the definition of a function", let's say f, although I acknowledge that such an error can be carried out using the hypothetical function when, mentioned above. I'm looking for a practical way to log the calls to f. As I see the topic, the maintenance of the log is a task of the program that makes use of f, not of f itself, mainly because it can be accomplished in many ways. To dictate any modality to f, simply invalidate its portability.

$\endgroup$
  • $\begingroup$ Can you relate what you want maybe to a feature in another programming language? $\endgroup$ – Searke Apr 6 '16 at 18:00
  • $\begingroup$ Thank for the reply! In a comment below I specify that the question has appeared in order to log / debug, therefore I think that it makes no sense in languages having a debugger capable to point at a single code line. $\endgroup$ – mitochondrial Apr 6 '16 at 19:05
  • 1
    $\begingroup$ Will this do the trick Clear[whenInvoked]; SetAttributes[whenInvoked, HoldAll]; whenInvoked[t : {__}] := (dosomthing; t) ? $\endgroup$ – Algohi Apr 6 '16 at 21:57
  • 1
    $\begingroup$ It seems you are looking for the Villegas-Gayley trick. $\endgroup$ – Alexey Popkov Apr 7 '16 at 4:43
  • $\begingroup$ To add to @Searke 's answers, I suppose you need to decide on the use case. Either the completed program should log calls to certain functions, then this side-effect should be included in the functions' definitions. Or you are looking for a profiler of some sort, which isn't really the default function of the front end of MMA. $\endgroup$ – LLlAMnYP Apr 7 '16 at 10:44
4
$\begingroup$

Here is what you requested. The basic idea is to edit the DownValues of a function you defined. Let's define the fibbonacci function as an example:

fib[1] = 1; 
fib[2] = 2; 
fib[n_] := fib[n - 1] + fib[n - 2]

We can see what the definition of fib internally looks like by using DownValues:

DownValues[fib]

{HoldPattern[fib[1]] :> 1, 
 HoldPattern[fib[2]] :> 2, 
 HoldPattern[fib[n_]] :> fib[n - 1] + fib[n - 2]}

Let's say we want to change the function so that it prints "test" whenever it is called. We can make a function that edits the DownValues:

functionChanger[a_ :> b_] := (a :> (Print["test"]; b))

And then let's make a function that applies this to the DownValues:

changeMyFunction[myFunction_] := (DownValues[myFunction] = 
   Map[functionChanger, DownValues[myFunction]])

We can now use this on fib:

changeMyFunction[fib]

{HoldPattern[fib[1]] :> (Print["test"]; 1), 
 HoldPattern[fib[2]] :> (Print["test"]; 2), 
 HoldPattern[fib[n_]] :> (Print["test"]; fib[n - 1] + fib[n - 2])}

Now "test" will print every time that fib is called.


This is all an incredibly bad idea. If you decide to code like this, don't be surprised if people avoid your code.

$\endgroup$
1
$\begingroup$

You shouldn't do this. This sounds like the kind of feature that smart people who don't have experience with software engineering dream up.

But, there are ways to do this. I think maybe the most reasonable (to me) would to make a decorator (most popularly known thru python) of sorts.

We can make a higher order function that does arbitrary things to a function. For example, it could print a message before evaluating the actual function. This example creates a decorator that warns that the function is Deprecated:

deprecated[function_] := 
    Function[args, 
        Module[{}, Print["This function has been deprecated"]; 
        function[args]]
    ]

We can use this when defining a function, like test here:

test = deprecated@
   Function[x, x + 1];

Or if we want to, we could define a new function using it.

$\endgroup$
  • $\begingroup$ Thank for the reply! (1) If I understand precisely your thought, you consider the requested feature a risky one. Do you believe the same if it is used only for logging / debugging goals ? I have omitted this part in my question because the focus was on the technical detail, but I'm considering to rewrite the question. (2) The solution you propose still forces to add code everywhere, instead of a single instruction placed at the beginning of the program. More than a simple wrap is needed. $\endgroup$ – mitochondrial Apr 6 '16 at 19:06
  • $\begingroup$ (1) Generally if something is a fundamentally useful feature or good idea you'll see an implementation of it in some language somewhere. It's good to reference that since you'll have a lot of information about the tradeoff adding that feature gives. $\endgroup$ – Searke Apr 6 '16 at 19:24
  • $\begingroup$ (1) You are fundamentally looking to alter the definition of a function. That's bad. Esp to make it more stateful. For built in functions you'd have to at least unprotect them. I'm sure there's some horrible hack you can do with $Pre, to get around this, but it's awful to think about $\endgroup$ – Searke Apr 6 '16 at 19:26
  • $\begingroup$ (2) No. You don't have to add code everywhere. You just have to define new functions if you want new behavior. $\endgroup$ – Searke Apr 6 '16 at 19:28
  • $\begingroup$ (3) Try sticking to the set of tools that professional programmers use. Stick to largely functional programming that minimizes global state. The answer to "Could I do this in a well designed programming language?" is "No". If I ever saw code that did this, I would absolutely refuse to work with it until it was rewritten. Professional programmers live without this feature and it's not because they couldn't implement it. $\endgroup$ – Searke Apr 6 '16 at 19:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.