30
$\begingroup$

It seems that Internal`AddHandler or other functions related to it can be quite helpful sometimes, especially when dealing with messages and such.

But the Handler series of functions is undocumented, so I hope maybe someone can give me a brief tutorial of this?


Edit 1

There's an answer which used this feature. There I can get some information about how to use it with Message information, but I think the knowledge I can get there is still quite limited, so maybe someone can give some further explanation?

Thanks!

$\endgroup$
3
  • $\begingroup$ Have you tried to contact @Szabolcs to ask him directly? $\endgroup$
    – Jens
    Commented Jul 9, 2016 at 3:54
  • $\begingroup$ Related: mathematica.stackexchange.com/a/99170/1871 $\endgroup$
    – xzczd
    Commented Mar 10, 2017 at 7:09
  • $\begingroup$ Maybe a dev from WRI could post a tutorial? Or maybe there's a Markdown file hidden somewhere in the application folder? $\endgroup$
    – M.R.
    Commented Feb 17, 2020 at 4:23

2 Answers 2

35
$\begingroup$

So I have a little bit more info, but mostly a learning technique we can test:

Init

I wanted to see when each of these was called so I made each possible handler (without a previous assignment) just call Print. Then I found that for "Wolfram.System.Print" this is a terrible idea as it is called anytime the system prints. So I removed that. Here's what I'm working off of:

With[{h = #},
    Internal`AddHandler[h, Print@(h -> {##}) &]
    ] & /@ 
  DeleteCases[Keys@Internal`Handlers[], 
   "VetoableValueChange" | "Wolfram.System.Print" | "ValueChange" | 
    "Message"
   ];

Message

"Message" works just as described by Szabolcs. Each time a possible message is generated Hold[mspec_Message, onQ] gets passed to your handlers.

MessageTextFilter

"MessageTextFilter" seems to be applied whenever Message is called (as best I can tell) which means it doesn't get applied for quieted messages. It takes the sequence template_String,Hold[mn_MessageName],Hold[mcall_Message] as its arguments.

If it returns a string, this is passed as the message text. For example:

ignoreSet[_, _, Hold[Message[Set::nosym, _]]] :=
  "Well this is fun";

Internal`AddHandler["MessageTextFilter",
 ignoreSet
 ]

_ = 1

Set::nosym: Well this is fun

NewSymbol

"NewSymbol" works pretty much just as $NewSymbol does. Everytime a symbol is added to Mathematica's symbol table the handlers get called, being passed {symName_String,context_String}.

RemoveSymbol

"RemoveSymbol" is the exact opposite. Everytime a symbol is removed from that table (I've only gotten this to happen with Remove) the same list gets passed to the handlers.

ValueChange

"ValueChange" can be very useful.

I set it like so:

Internal`AddHandler["ValueChange", (Print["ValueChange" -> {##}] &)];

The symbol in question needs to be tracked internally for it to work, though. I.e. Internal`TrackExpression or Internal`SetValueTrackExtra needs to have been called on it so that it's in Internal`GetTrackedSymbols[].

It seems to remove the symbol from Internal`GetTrackedSymbols[] after a single call.

The change cases I've seen look like:

Internal`TrackExpression[b, 1];
Clear[b]

ValueChange->{HoldComplete[b,Clear]}

Internal`TrackExpression[b, 1];
Block[{b}, b]

ValueChange->{HoldComplete[b,Block,True]}

ValueChange->{HoldComplete[b,Block,False]}

1

Internal`TrackExpression[b, 1];
Block[{b = 1}, b]

ValueChange->{HoldComplete[b,Block,True]}

ValueChange->{HoldComplete[b,Null,1,OwnValues]}

1

Internal`TrackExpression[b, 1];
b = 1

ValueChange->{HoldComplete[b,1,1,OwnValues]}

1

Internal`TrackExpression[b, 1];
b[a_] := c;
Clear[b]

ValueChange->{HoldComplete[b,b[a_],c,Removed[$$Failure],DownValues]}

Internal`TrackExpression[b, 1];
z[b[a_]] ^:= c;
Clear[b]

ValueChange->{HoldComplete[b,z[b[a_]],c,Removed[$$Failure],UpValues]}

Internal`TrackExpression[b, 1];
b[a_][] := c;
Clear[b]

ValueChange->{HoldComplete[b,b[a_][],c,Removed[$$Failure],SubValues]}

Note that this will inundate you with change notifications whenever the usage popup menu shows (since there are a pile of FE tracked values there)

One thing to note is that this is part of how Dynamic variables are handled:

Dynamic[a]

a

a = 1

ValueChange->{HoldComplete[a,1,1,OwnValues]}

1

ValueTrackTrigger

"ValueTrackTrigger" works much like "ValueChange". They get the same arguments and are called under similar circumstances, except "ValueTrackTrigger" is called only when values are truly changed and after "ValueChange"

VetoableValueChange

I've never seen this one in action, but it's worth looking at the handler it comes pre-populated with: JLink`Private`jlinkVetoFunction. Looking at the DownValues on this:

HoldPattern[
  JLink`Private`jlinkVetoFunction[
   HoldComplete[JLink`Private`sym_Symbol, JLink`Private`lhs_, 
    JLink`Private`rhs_, _, 
    DownValues]]] :> (JLink`CallJava`Private`setField[
    JLink`Private`lhs, JLink`Private`rhs]; False) /; 
  JavaObjectQ[Unevaluated[JLink`Private`sym]]

One sees that the handler should take the argument HoldComplete[sym_Symbol,lhs_,rhs_,<no_clue_what_this_is>,DownValues] for setting a down value. And it can clearly reject this proposed change, as the callback does, by returning False. For some reason even after adding the handler:

Internal`AddHandler["VetoableValueChange",
 Print@("VetoableValueChange" -> {##}) &
 ]

I still haven't seen this in action. Might be print protected??

GetFileEvent

"GetFileEvent" I don't understand completely but I've found a few distinct usages: HoldComplete[fname_String,Identity,First] at the start of a Get call and HoldComplete[fname_String,Identity,Last] at the end (I'm sure there's more meaning in there, I just don't know what it is). And when I did a documentation search with cmd+F I got these two: HoldComplete["DocumentationSearch`DocumentationSearch`", Identity, Last] and HoldComplete[DocumentationSearch`,Identity,Last]. Maybe these are special GetFileEvents Mathematica records?

Misc.

The two I was most eager to see in action are "GetSymbol" and "SetSymbol" but I have yet to see them called. I'll update this with more stuff when I next know what's up.

Cleanup

When I'm done testing I remove all of these handlers with:

With[{h = #},
    Internal`RemoveHandler[First@h, #] & /@ Last@h
    ] & /@ Rest@Internal`Handlers[];
$\endgroup$
1
  • 2
    $\begingroup$ SUCH a late but AWESOME answer. Hope you can get enough upvotes. Thanks a lot! $\endgroup$
    – Wjx
    Commented Mar 10, 2017 at 10:55
5
$\begingroup$

2023 update

There is an officially published function, which helps to work with Handlers

Experimental`ValueFunction[symbol] = HandlerFunction
$\endgroup$

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