6
$\begingroup$

Apologies for a less than ideal question, but if I could have asked a good question the answer would probably have been revealed in the process...

In answer to this question @LeonidShifrin gave some - to me very clever - code to allow the name of the currently executing function to be obtained at runtime, with

SetAttributes[deflex, HoldAll];
deflex /: SetDelayed[deflex[f_[args___]], rhs_] :=  
      Hold[rhs] /. HoldPattern[currentFunction] :> f /. Hold[code_] :> SetDelayed @@ Hold[f[args], code];
Protect[deflex];

used as deflex @...

I thought I could use this idea to create a debugger of sorts, because I need something better than screen debugging with print etc. and, even IF I could get to grips with the MMA "debugger" it slows down execution so much on my complex code + irreducibly large data quantities that debugging becomes impossible in any reasonable time scale.

The alternative and thus far unsuccessful approach I had in mind was optimistically simplistic:

  1. define a global association linking symbols to deferred-execution debugging code

  2. define a debugMe[symboltoDebug_] function which, when the currently executing function was found in the global association, would then execute the code specified by the global association

However, I couldn't really work out how deflex worked and was therefore not able to apply the same ideas, instead I tried something else, and the question is - for both educational and functional purposes - how can I inject debugging code in such a way that it can be supplied outside a function but when evaluated will use the appropriate local variables so that debugging can be made conditional on local evaluations and provide arbitrary debugging output...

Finally, having finally worked out that one can resume from Interrupt[] by F5 (question here thought OT but v. useful to me), I naturally wanted the debugging code to test a condition, perform some debugging action and then call Interrupt[] so that I could explore further - and possibly redefine the debug conditions etc. in the subsession before resuming.

MNWE

Function to be debugged

ClearAll[complicated];
Options[complicated] = {"debug"->True};
deflex @ complicated[a_Integer, b_List, c_String, OptionsPattern[]]:=
    Module[{nowt = {}},
        Table[Activate[debugger[currentFunction]]; c <> ToString[{a, loopIdx}], {loopIdx, b[[1]], b[[2]], b[[3]]}]
    ];

debug functions etc.

gSymbolsToDebug = <|
  complicated -> Inactivate[If[loopIdx == b[[1]], Interrupt[]];]|>
ClearAll[debugMe];
debugMe[symbol_]:= Inactivate[If[Not@MissingQ@gSymbolsToDebug[symbol], Print["symbol in debug list"]; gSymbolsToDebug[symbol], Print["symbol NOT in debug list"];]];

ClearAll[debugger];
SetAttributes[debugger, HoldAll];
debugger[currentFunction_]:=  Inactivate[If[OptionValue[currentFunction, "debug"] == True, debugMe[currentFunction]]];

Alas, when I evaluating complicated[2, {1, 7, 2}, "oops", "debug" -> True] gives the following without being Interrupted.

{"oops{2, 1}", "oops{2, 3}", "oops{2, 5}", "oops{2, 7}"}

My problems clearly include poor understanding of how to not evaluate expressions via HoldAll, Inactivate, etc. and of course, how @leonid-shiffrin's code worked in the first place.

Given all that, how might I fulfil the debugging goal?

$\endgroup$

Your Answer

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

Browse other questions tagged or ask your own question.