How useful is it to program a user-built function in a package to produce a red warning message F::argx if you give the wrong number of arguments to that function? How do I do this?
- 20,207
- 3
- 39
- 74
- 1,283
- 7
- 14
-
1have you seen this? – acl Apr 15 '13 at 18:52
4 Answers
The large addendum on handling multiple messages as built-ins do has been moved to a separate post; please see the link below for advanced message handling options.
Macro package function SetArgumentCount
In recent versions there is an undocumented function Macros`SetArgumentCount that specifically automates creation of a definition for an argument Message. Examples:
Macros`SetArgumentCount[foo, 2]
foo[1]
foo[1, 2, 3]
foo::argr: foo called with 1 argument; 2 arguments are expected.
foo::argrx: foo called with 3 arguments; 2 arguments are expected.
Macros`SetArgumentCount[foo, {2, 4}]
foo[1]
foo[1, 2, 3, 4, 5]
foo::argbu: foo called with 1 argument; between 2 and 4 arguments are expected. >>
foo::argb: foo called with 5 arguments; between 2 and 4 arguments are expected. >>
(Returned input intentionally omitted.)
Available messages
The message ::argx is one of the general messages intended for use with any function. These have the special property of being called for any symbol used (placed left of ::):
Message[foo::"argx", "foo", 2, 3]
foo::argx: foo called with 2 arguments; 1 argument is expected. >>
Use Messages[General] to see a list of these messages.
Synax highlighting
SyntaxInformation can be used to create the red syntax highlighting present in many built-in functions. For a full description please see:
Basic usage
You may have noticed that the method shown in R.M's answer doesn't produce behavior that exactly matches internal functions such as Plot, which echo bad input:
Plot[1, 2, 3, 4]
Plot::nonopt: Options expected (instead of 4) beyond position 2 in Plot[1,2,3,4]. An option must be a rule or a list of rules. >>
Plot[1, 2, 3, 4]
To get this you behavior you can generate the Message as a side-effect, as in Condition:
ClearAll@f
SyntaxInformation[f] = {"ArgumentsPattern" -> {_}};
f[1] := True
f[_] := False
f[x___] /; Message[f::argx, "f", Length@{x}] := Null
Now:
f[1, 2, 3]
f::argx: f called with 3 arguments; 1 argument is expected. >>
f[1, 2, 3]
The final no-match rule can also be written:
x_f /; Message[f::argx, "f", Length @ Unevaluated @ x] := Null
-
You said it didn't produce the same behaviour as in built-ins and that using condition was better... I just don't see it in the example – rm -rf Apr 15 '13 at 21:01
-
3@rm-rf Oh. Your catch definition actually matches and returns
Message[f::argx, "f", Length@{x} + 1]or rather theNullit produces; my catch definition does not match and produces the message as a side-effect, and therefore the original input is returned. This is how most internal definitions are written. – Mr.Wizard Apr 15 '13 at 21:02 -
1
-
btw, I just remembered why I don't use this in my packages... if you have different messages being thrown based on the form of the input (as I often have), then throwing messages as a side-effect of not matching the form will result in all messages being thrown. – rm -rf Apr 16 '13 at 14:32
-
@rm-rf please see my update and tell me if it makes sense as written. – Mr.Wizard Apr 16 '13 at 19:43
-
Thanks for the update! I'm a little busy now, but will definitely read it later in the day – rm -rf Apr 16 '13 at 20:02
-
@Mr.Wizard, you expose here a great summary of techniques, thank you for this. – faysou Apr 16 '13 at 21:42
-
@Faysal Thanks, and you're welcome. It's been a while since I've used these methods in my own code so hopefully I didn't make too many mistakes here; please let me know if you find any. – Mr.Wizard Apr 16 '13 at 22:02
-
1Sidenote:
ArgumentCountQlets you be more declarative and takes care of selecting the message argx, argtu, argrx, according to the number of arguments required. E.g,f[i___] /; ArgumentCountQ[f, System`FEDump`NonOptionArgCount[{i}], {2, 5}] := Null– Rojo Jul 21 '13 at 11:37 -
-
I thought
func[___]?funcmsgwas clever before, I think_func?funcmsgis superior. – rcollyer Jul 15 '14 at 13:54 -
If I want to define a head f:
f[a_,b_*c_]:=... ;f[a_*b_,g[c_]]:=...;How to define my syntax-checking function? – WateSoyan May 15 '15 at 11:02 -
@WateSoyan I would need more information to properly answer that. Why don't you post a new question explaining your function and what you wish to accomplish regarding syntax-checking and message generation? – Mr.Wizard May 15 '15 at 11:45
-
Though my function here is fabricate , indeed I often have this need. – WateSoyan May 15 '15 at 12:08
-
Dear Mr.Wizard, In the section "Basic usage",
f[x___] /; Message[f::argx, "f", Length@{x}] := Null, However, I cannot understand whyf[1,2,3]gives the warning information. Could you help me to understand it? Thks:) – xyz Jun 22 '15 at 07:32 -
Accodrding the usage of
/;in DOC,lhs:=rhs/;testis a definition to be used only iftestyieldsTrue. – xyz Jun 22 '15 at 07:41 -
2@ShutaoTang The
Messageexpression is evaluated as part of theCondition.Messageitself returnsNull, therefore the condition does not match, but by that time the message has already been printed. The RHS is irrelevant in this case. – Mr.Wizard Jun 22 '15 at 07:42 -
In addition , I cannot find the definition
HiddenOptionsin the auxiliary functionoptCheck– xyz Jun 22 '15 at 07:44 -
@ShutaoTang
hiddenOptionsis not really significant here; it holds internal options that are not intended to be set by the user. In version 10.1.0 you can see these forArrayPlotwithDownValues[Graphics`ArrayPlotDump`Private`hiddenOptions]. – Mr.Wizard Jun 22 '15 at 07:51 -
@Mr.Wizard, In my V10.1,
DownValues[Graphics`ArrayPlotDump`Private`hiddenOptions]gives the result{}– xyz Jun 22 '15 at 08:02 -
Dear Mr.Wizard, Now I have a small request. Could you use the
Catch-all methodorFall-through methodto deal with my qestion like theself-contained exampleyou give in the end of you answer. And make it has the effect like this – xyz Jun 22 '15 at 08:10 -
@ShutaoTang I'll take another look at try to provide a useful example. – Mr.Wizard Jun 22 '15 at 09:12
-
@ShutaoTang I am too sleepy to do this properly right now, and also this answer needs updating too. I'll try to get around to it tomorrow. – Mr.Wizard Jun 22 '15 at 10:11
-
As acl points out, this post shows you how to setup error highlighting for invalid number of arguments. Coming to the actual error messages used, there are three built-in messages attached to General, that can be used for your own functions as well. These are argx, argrx and argt:
General::argx
(* "`1` called with `2` arguments; 1 argument is expected." *)
General::argrx
(* "`1` called with `2` arguments; `3` arguments are expected." *)
General::argt
(* "`1` called with `2` arguments; `3` or `4` arguments are expected." *)
You can attach these messages to your own functions (any message defined for General can be used for any other symbol) like in the following example (shown only for argx):
ClearAll@f
SyntaxInformation[f] = {"ArgumentsPattern" -> {_}};
f[1] := True
f[_] := False
f[_, x__] := Message[f::argx, "f", Length@{x} + 1]

There's a nice internal method for this that has existed since at least 10.4: System`Private`ArgumentsWithRules.
You can use it to define a nicely handled function like so:
f[args___] :=
With[
{
a =
System`Private`ArgumentsWithRules[
f[args](*function with args to gather from*),
{
0(*min length*),
1(*max length*)
},
Hold(*head to wrap on the lists*)
]
},
yourFunctionBody /; Length[a] > 0
]
f[1, 2]
f::argt: f called with 2 arguments; 0 or 1 arguments are expected.
f[1, 2]
Even better, if f has Options this detects it:
Options[f] = {"a" -> "b"};
f[1, 2]
f::nonopt: Options expected (instead of 2) beyond position 1 in f[1,2]. An option must be a rule or a list of rules.
f[1, 2]
This makes it useful extension on ArgumentCountQ:
ArgumentCountQ[f, 3, 1, 2]
f::argt: f called with 3 arguments; 1 or 2 arguments are expected.
False
- 46,870
- 3
- 92
- 239
-
Do you know what is the difference between
System`Private`ArgumentsandSystem`Private`ArgumentsWithRules? They seem to behave in the same way. Also, it seems to color thefblack in the front end just be calling it likeSystem`Private`ArgumentsWithRules[f[a, b], {1, 2}, g]from a fresh kernel. – QuantumDot Aug 06 '18 at 14:24 -
@QuantumDot
ArgumentsWithRulesprovides options checking for us. See this and this – b3m2a1 Aug 06 '18 at 17:34 -
Hmm.. it seems
Argumentsitself can provide option checking:Options[f] = {opt1 -> x}; System`Private`Arguments[f[a, b, c], {1, 2}, g]– QuantumDot Aug 06 '18 at 19:45 -
@QuantumDot ah hmm, I remember the difference being subtle. It might be just in the message or the return or something. Just pick one and be consistent I figure. – b3m2a1 Aug 06 '18 at 20:03
Thats what I learned.
To see if my function has just 2 arguments, not 1 argument and no more the 2 arguments I did these steps:
Define the warnings
General::twoplus = "f called with to much arguments, 2 argument expected.";
General::twominus = "f called with 1 argument, 2 arguments expected.";
f[_, _, x__] := Message[f::twoplus, "f", Length@{x} + 1]
f[x__] := Message[f::twominus, "f", Length@{x} + 1]
Define the function
f[x_, y_] := x + y + 1;
Call the function
f[1]
f::twominus: f called with 1 argument, 2 arguments expected.
f[1, 2]
4
f[1, 2, 3]
f::twoplus: f called with to much arguments, 2 argument expected.
- 1,283
- 7
- 14
-
Some things to consider: (1) Your messages have no placeholders; nothing is being filled in from the second and following arguments of
Message. You can use merelyMessage[f::twominus]if you want the message printed verbatim. (2) You may wish to define the message forfrather than forGeneralunless you intend to use the same messages for other functions as well. – Mr.Wizard Apr 18 '13 at 09:26