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?
-
1$\begingroup$ have you seen this? $\endgroup$– aclCommented Apr 15, 2013 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
-
$\begingroup$ 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 $\endgroup$– rm -rf ♦Commented Apr 15, 2013 at 21:01
-
3$\begingroup$ @rm-rf Oh. Your catch definition actually matches and returns
Message[f::argx, "f", Length@{x} + 1]
or rather theNull
it 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. $\endgroup$ Commented Apr 15, 2013 at 21:02 -
1$\begingroup$ Ah, it keeps the input unevaluated. +1 $\endgroup$– rm -rf ♦Commented Apr 15, 2013 at 21:06
-
1$\begingroup$ Sidenote:
ArgumentCountQ
lets 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
$\endgroup$– RojoCommented Jul 21, 2013 at 11:37 -
2$\begingroup$ @ShutaoTang The
Message
expression is evaluated as part of theCondition
.Message
itself returnsNull
, therefore the condition does not match, but by that time the message has already been printed. The RHS is irrelevant in this case. $\endgroup$ Commented Jun 22, 2015 at 7:42
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
-
$\begingroup$ Do you know what is the difference between
System`Private`Arguments
andSystem`Private`ArgumentsWithRules
? They seem to behave in the same way. Also, it seems to color thef
black in the front end just be calling it likeSystem`Private`ArgumentsWithRules[f[a, b], {1, 2}, g]
from a fresh kernel. $\endgroup$ Commented Aug 6, 2018 at 14:24 -
-
$\begingroup$ Hmm.. it seems
Arguments
itself can provide option checking:Options[f] = {opt1 -> x}; System`Private`Arguments[f[a, b, c], {1, 2}, g]
$\endgroup$ Commented Aug 6, 2018 at 19:45 -
$\begingroup$ @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. $\endgroup$– b3m2a1Commented Aug 6, 2018 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.
-
$\begingroup$ 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 forf
rather than forGeneral
unless you intend to use the same messages for other functions as well. $\endgroup$ Commented Apr 18, 2013 at 9:26