24
$\begingroup$

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?

$\endgroup$
  • 1
    $\begingroup$ have you seen this? $\endgroup$ – acl Apr 15 '13 at 18:52
24
$\begingroup$

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
$\endgroup$
  • $\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 Apr 15 '13 at 21:01
  • 3
    $\begingroup$ @rm-rf Oh. Your catch definition actually matches and returns Message[f::argx, "f", Length@{x} + 1] or rather the Null 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$ – Mr.Wizard Apr 15 '13 at 21:02
  • 1
    $\begingroup$ Ah, it keeps the input unevaluated. +1 $\endgroup$ – rm -rf Apr 15 '13 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$ – Rojo Jul 21 '13 at 11:37
  • 2
    $\begingroup$ @ShutaoTang The Message expression is evaluated as part of the Condition. Message itself returns Null, therefore the condition does not match, but by that time the message has already been printed. The RHS is irrelevant in this case. $\endgroup$ – Mr.Wizard Jun 22 '15 at 7:42
15
$\begingroup$

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]

$\endgroup$
5
$\begingroup$

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
$\endgroup$
  • $\begingroup$ Do you know what is the difference between System`Private`Arguments and System`Private`ArgumentsWithRules? They seem to behave in the same way. Also, it seems to color the f black in the front end just be calling it like System`Private`ArgumentsWithRules[f[a, b], {1, 2}, g] from a fresh kernel. $\endgroup$ – QuantumDot Aug 6 '18 at 14:24
  • $\begingroup$ @QuantumDot ArgumentsWithRules provides options checking for us. See this and this $\endgroup$ – b3m2a1 Aug 6 '18 at 17:34
  • $\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$ – QuantumDot Aug 6 '18 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$ – b3m2a1 Aug 6 '18 at 20:03
1
$\begingroup$

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.  
$\endgroup$
  • $\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 merely Message[f::twominus] if you want the message printed verbatim. (2) You may wish to define the message for f rather than for General unless you intend to use the same messages for other functions as well. $\endgroup$ – Mr.Wizard Apr 18 '13 at 9:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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