Attaching the same message to several symbols
How about doing something as simple as
sym1::msg = "I am feeling grumpy.";
sym2::msg = sym3::msg = sym1::msg;
Then
Do[With[{ff = f},
Message[MessageName[ff, "msg"]]], {f, {sym1, sym2, sym3}}]
Outputs
sym1::msg: I am feeling grumpy.
sym2::msg: I am feeling grumpy.
sym3::msg: I am feeling grumpy.
Messages defined for a symbol are stored in list of rules Messages[sym]
similar to DownValues
, UpValues
etc. We can use this fact to create general message inheritance mechanism.
Let's start with defining custom message on our own general
symbol:
ClearAll[general]
general::myMessage = "text of general myMessage"
As expected this message is stored in a list as a delayed rule:
Messages[general]
(* {HoldPattern[general::myMessage] :> "text of general myMessage"} *)
Now let's try to define a symbol which will inherit messages from our general
symbol. First attempt would be:
ClearAll[testSym]
MessageName[testSym, name_] := MessageName[testSym, name]
(* During evaluation of In[4]:= Message::name: Message name testSym::name_ is not of the form symbol::name or symbol::name::language. >>
$Failed *)
It fails since MessageName
is protected from passing non-string as second argument. Also attempt of assigning anything else than explicit string fails:
testSym::myMessage := general::myMessage
(* During evaluation of In[6]:= MessageName::messg: testSym::myMessage cannot be set to general::myMessage. It must be set to a string. >>
$Failed *)
But nothing prevents us from adding hand crafted rule to Messages
list:
AppendTo[Messages[testSym],
HoldPattern[MessageName[testSym, name_]] :> MessageName[general, name]
]
(* {HoldPattern[testSym::name_] :> general::name} *)
There were no Message::name
warnings since none of MessageName
expressions was evaluated. The one on left hand side is wrapped in HoldPattern
and the one on the right is protected from evaluation by RuleDelayed
. They will be evaluated only when called:
MessageName[testSym, "myMessage"]
(* "text of general myMessage" *)
Message[testSym::myMessage]
(* During evaluation of In[9]:= testSym::myMessage: text of general myMessage *)
Problem arises when we want to call message from "real" General
symbol on our inheriting symbol:
Message[testSym::argctu, testSym]
(* A lot of StringSplit::strse, StringJoin::string, StringForm::string errors *)
It fails since, where Message
was expecting a string, it go MessageName[general, argctu]
, which does not evaluate to a string. We can easily prevent such situations by adding a Condition
to our "inheritance" rule, such that rule matches only when MessageName[general, name]
evaluates to a string.
ClearAll[testSym]
AppendTo[Messages[testSym],
HoldPattern[MessageName[testSym, name_]] :>
MessageName[general, name] /; StringQ[MessageName[general, name]]
]
(* {HoldPattern[testSym::name_] :> general::name /; StringQ[general::name]} *)
Now everything works as expected. If message is not defined on testSym
, but is defined on general
, message from general
is used:
Message[testSym::myMessage]
(* During evaluation of In[13]:= testSym::myMessage: text of general myMessage *)
If message is not defined neither on testSym
nor on general
, ordinary delegation to "real" General
is used:
Message[testSym::argctu, testSym]
(* During evaluation of In[14]:= testSym::argctu: testSym called with 1 argument. >> *)
If we explicitly define a message on testSym
, normal pattern specificity applies and message from testSym
is used:
testSym::myMessage = "text of testSym myMessage";
Message[testSym::myMessage]
(* During evaluation of In[15]:= testSym::myMessage: text of testSym myMessage *)
Usage in a package
BeginPackage["ToyPackage`"]
ToyFunc1::usage = ""
ToyFunc2::usage = ""
$ToyVariable::usage = ""
$ToyVariableWithMessages::usage = ""
Begin["`Private`"]
general::myMessage = "general myMessage"
general::myMessageWithArg = "general myMessageWithArg: `1`"
ToyFunc1::myMessage = "ToyFunc1 myMessage"
ToyFunc2::myMessageWithArg = "ToyFunc2 myMessageWithArg: `1`"
$ToyVariable = "$ToyVariable value"
$ToyVariableWithMessages = "$ToyVariableWithMessages value"
Function[
sym
,
Quiet[MessageName[sym, name_] =., Unset::norep];
AppendTo[Messages[sym],
HoldPattern[MessageName[sym, name_]] :>
MessageName[general, name] /;
StringQ[MessageName[general, name]]
]
,
HoldFirst
] @@@
(ToExpression[#, InputForm, Hold]&) /@
DeleteCases[Names["ToyPackage`*"], "$ToyVariable"]
End[]
EndPackage[]
ToyFunc1
inherits from general
and overrides myMessage
:
Message[ToyFunc1::myMessage]
(* During evaluation of In[1]:= ToyFunc1::myMessage: ToyFunc1 myMessage *)
Message[ToyFunc1::myMessageWithArg, "arg"]
(* During evaluation of In[2]:= ToyFunc1::myMessageWithArg: general myMessageWithArg: arg *)
Message[ToyFunc1::argctu, ToyFunc1]
(* During evaluation of In[3]:= ToyFunc1::argctu: ToyFunc1 called with 1 argument. >> *)
ToyFunc2
inherits from general
and overrides myMessageWithArg
:
Message[ToyFunc2::myMessage]
(* During evaluation of In[4]:= ToyFunc2::myMessage: general myMessage *)
Message[ToyFunc2::myMessageWithArg, "arg"]
(* During evaluation of In[5]:= ToyFunc2::myMessageWithArg: ToyFunc2 myMessageWithArg: arg *)
Message[ToyFunc2::argctu, ToyFunc1]
(* During evaluation of In[6]:= ToyFunc2::argctu: ToyFunc1 called with 1 argument. >> *)
$ToyVariable
was explicitly excluded from inheritance:
Message[$ToyVariable::myMessage]
(* During evaluation of In[7]:= $ToyVariable::myMessage: -- Message text not found -- *)
Message[$ToyVariable::myMessageWithArg, "arg"]
(* During evaluation of In[8]:= $ToyVariable::myMessageWithArg: -- Message text not found -- (arg) *)
Message[$ToyVariable::argctu, $ToyVariable]
(* During evaluation of In[9]:= $ToyVariable::argctu: $ToyVariable value called with 1 argument. >> *)
$ToyVariableWithMessages
inherits from general
and doesn't override anything:
Message[$ToyVariableWithMessages::myMessage]
(* During evaluation of In[10]:= $ToyVariableWithMessages::myMessage: general myMessage *)
Message[$ToyVariableWithMessages::myMessageWithArg, "arg"]
(* During evaluation of In[11]:= $ToyVariableWithMessages::myMessageWithArg: general myMessageWithArg: arg *)
Message[$ToyVariableWithMessages::argctu, $ToyVariableWithMessages]
(* During evaluation of In[12]:= $ToyVariableWithMessages::argctu: $ToyVariableWithMessages value called with 1 argument. >> *)