9
$\begingroup$

I know that Check[expr,failexpr] is a mechanism to catch messages. The problem with it is that if a message is generated within expr, Check[expr,failexpr] will return failexpr, and the return value of expr will be lost. Sometimes messages are just warnings and do not imply that the return value of a computation will be garbage. So what I want is a mechanism to detect messages within an expression without losing the return value of the expression. Through this mechanism I intend to Print some additional information to the screen, that will allow me to debug the Message. How can I do this?

$\endgroup$
1
  • $\begingroup$ Incidentally have you seen these?: (8215), (20367) $\endgroup$
    – Mr.Wizard
    Oct 17, 2014 at 22:33

3 Answers 3

4
$\begingroup$

One can define a dynamic environment where Message will be overloaded. Here is one way:

ClearAll[withMessageDetection];
SetAttributes[withMessageDetection, HoldAll];
withMessageDetection[code_] :=
  Internal`InheritedBlock[{Message},
    Module[{inMessage, tag},
      Unprotect[Message];
      (call : Message[args___]) /; ! TrueQ[inMessage] :=  
        Block[{inMessage = True},
          Sow[{args}, tag];
          call
        ];
      Protect[Message];
      Reap[code, tag, #2 &]
    ]
  ]

For example:

withMessageDetection[Range[10][[15]]]

During evaluation of In[676]:= Part::partw: Part 15 of {1,2,3,4,5,6,7,8,9,10} does not exist. >>

(* {{1,2,3,4,5,6,7,8,9,10}[[15]],{{{Part::partw,15,{1,2,3,4,5,6,7,8,9,10}}}}} *)

If you just want to print the information, you can use Print instead or together with Sow and Reap.

EDIT

A different version addressing the spec clarification in comments:

ClearAll[withMessageDetectionAlt];
SetAttributes[withMessageDetectionAlt, HoldAll];
withMessageDetectionAlt[code_, msgCode_] := 
  Internal`InheritedBlock[{Message},
    Module[{inMessage, messageWasIssued},
      Unprotect[Message];
      (call : Message[args___]) /; ! TrueQ[inMessage] := 
        Block[{inMessage = True},
          messageWasIssued = True;
          call
        ];
      Protect[Message];
      messageWasIssued = False;
      With[{result = code},
        If[messageWasIssued , msgCode];
        result
      ]
    ]
  ];

For example:

withMessageDetectionAlt[Range[10][[15]], Print["Message issued!"]]

During evaluation of In[1423]:= Part::partw: Part 15 of {1,2,3,4,5,6,7,8,9,10} does not exist. >>

During evaluation of In[1423]:= Message issued!

(* {1,2,3,4,5,6,7,8,9,10}[[15]] *)
$\endgroup$
4
  • $\begingroup$ The interface I had in mind was something like withMessageDetection[expr1, expr2], which always returns the result of expr1, and evaluates expr2 (ignoring its return value, but Print inside expr2 still has effect) if and only if Messages are generated inside expr1. $\endgroup$
    – a06e
    Oct 17, 2014 at 14:45
  • $\begingroup$ @becko I think, this spec is underdesigned. What if more than one message is generated during the evaluation? Do you want to evaluate expr2 on every generated message? Also, you will probably need to somehow pass the information about the currently generated message to the expr2. One way of doing this would be to use the syntax withMessageDetection[expr1, {msginfo_Symbol,expr2}], where you refer to whatever symbol you pass as msginfo, inside expr2, if you need that. Another way would be to make expr2 a function of those message parameters. Let me know which you'd prefer. $\endgroup$ Oct 17, 2014 at 15:08
  • $\begingroup$ expr2 gets evaluated only once after expr1 has completed, if and only if expr1 generates one or more messages. expr2 doesn't need to know the specific messages generated by expr1 (those will get printed to $Messages, so I will see them there if I have to). $\endgroup$
    – a06e
    Oct 17, 2014 at 21:44
  • $\begingroup$ @becko See my edit. $\endgroup$ Oct 17, 2014 at 22:11
5
$\begingroup$

A simple way would be to assign the value of expr to a variable and then return that variable after printing the messages. For example:

info::values = "n was `` and d was ``";

Module[{n, d, v}
, n = 100
; d = 0
; Check[v = n / d, Message[info::values, n, d]; v]
]

result screenshot

$\endgroup$
4
$\begingroup$

EvaluationData[expr] returns both the result of evaluating expr as well as the list of messages in various forms, Print output, and even timing information.

Example:

evd=EvaluationData[1/0]
During evaluation of In[85]:= Power::infy: Infinite expression 1/0 encountered.
Out[85]= <|Result:>ComplexInfinity,Success->False,FailureType->MessageFailure,OutputLog->{},Messages->{Power::infy},MessagesText->{                                  1
Power::infy : Infinite expression - encountered.
                                  0},MessagesExpressions->{Hold[Message[Power::infy,1/0]]},Timing->0.,AbsoluteTiming->0.001,InputString:>ToString[Unevaluated[1/0],InputForm]|>

You'll have to pull the result out via evd["Result"] but it's a very useful mechanism, introduced in version 10 in 2014, right around the time you were asking this question.

$\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.