10
$\begingroup$

It is often that one wonders which arguments are evaluated before being passed into a function. I would like to know if it is possible to see which argumets are being passed into a function without recoding the function, that is, outside of the function. I know that one could try with the Trace command, but it always puzzles me whether the output one really sees reflects what is being sent into the function prior to its evaluation.

I suppose, that the key issue is being able to draw a clear line, and say, "yes, these expressions are being fed into the function, and, aha, these evaluations are done in the function". Is there a generic way of doing this without recoding the function (that is, one could put a bunch of print statements in the definition of the function)?

For example, I might want to know which arguments are actually passed to a built-in Mathematica function (which one cannot recode) and might need to know what is done with these arguments within the function code.

I suppose that the Trace command could be used, but I never figured out how.

$\endgroup$
1
  • 1
    $\begingroup$ A fairly simple method is to do Unprotect[foo]; foo[args___] := Null /; (Print["evaluating foo on arguments ", {args}]; False). Can be verbose though, if a your foo is heavily used. $\endgroup$ Commented Feb 28, 2014 at 16:24

2 Answers 2

9
$\begingroup$

Basically, you have at least two options. One is to use Trace, and there have been many discussions here on SE about how to make the representation of the output of Trace convenient.

A complementary approach is to set up some code which would spy on what is being passed to a particular function. It may have an advantage over Trace in that it will only collect the arguments passed to a specific function.

Here is a possible code for the spying functionality, based on Villegas-Gayley technique:

ClearAll[modify];
SetAttributes[modify,HoldRest];
modify[f_Symbol,code_]:=
    (Unprotect[f];code;Protect[f])


ClearAll[$tag,spy,unspy];
    spy[f_,spyF_]:=
        Module[{inF},            
            unspy[f];
            $tag[f]=inF;
        modify[                
            f,
            call:f[args___]/;!TrueQ[inF]:=
                Block[{inF=True},
                    spyF[args];call
                ]
        ];
    ];

unspy[f_Symbol]:=
    With[{tag=$tag[f]},            
            If[Head[$tag[f]]===Symbol,($tag[f])=.];
        modify[
            f,DownValues[f]=DeleteCases[DownValues[f],def_/;!FreeQ[def,tag]]
        ]
    ]

( note that some imperfections in formatting are due to the SE editor bug related to the presence of $ characters in code).

Here is an example of how this can be used:

spy[With, Function[Null, Sow[Hold[##]], HoldAll]]
Reap[With[{a = 1, b = 2}, a + b]]

(* {3, {{Hold[{a = 1, b = 2}, a + b]}}}  *)

unspy[With]

(* {"With"} *)

Now, here are some convenience functions to automate this a bit:

ClearAll[sowArgs, getArgs, spyArgs, $heldTag];
    $heldTag[f_] := Hold[$tag[f]] /. DownValues[$tag];
sowArgs[f_] := Function[Null, Sow[Hold[##], $heldTag[f]], HoldAll];
    getArgs[f_] := Function[code, Reap[code, $heldTag[f], #2 &][[2]], HoldAll];
spyArgs[f_] := spy[f, sowArgs[f]];

(same comment on formatting here). Now you can use them as:

spyArgs[With]

getArgs[With][With[{a = 1, b = 2}, a + b]]

(* {{Hold[{a = 1, b = 2}, a + b]}} *)

unspy[With]

(* {"With"} *)
$\endgroup$
9
$\begingroup$

You can watch the evaluations of a function using On:

SetAttributes[zot, HoldFirst]
zot[x_, y_] := {x, y}

On[zot]
In[11]:= zot[1+1, 2+2] During evaluation of In[11]:= zot::trace:zot[1+1,2+2] --> zot[1+1,4]. During evaluation of In[11]:=zot::trace: zot[1+1,4] --> {1+1,4}. Out[11]= {2,4}

Here we can see the original unevaluated arguments to zot and also the partially evaluated arguments taking into account HoldFirst. We can also see that zot returns a value containing the unevaluated expression 1+1, the evaluation of which occurs after zot has returned. Such subtleties can be important in some contexts, such as code generation.

Use Off to turn off these tracing messages:

Off[zot]

On is easy to use, but we get mixed results when trying to trace built-in functions as they sometimes bypass the usual evaluation machinery.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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