Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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?

share|improve this question
1  
have you seen this? –  acl Apr 15 '13 at 18:52
add comment

3 Answers

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.

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

Handling multiple messages

R.M comments that he doesn't use this form in packages because with multiple messages defined all will be issued. Often this is desired as if there are multiple problems with the arguments it makes sense to inform the user of all of them at once. If however more control is needed, while retaining the canonical behavior of returning an unmatched expression, an axillary function can be used to handle argument testing and message generation.

This axillary function can be used either:

  1. to check the arguments of the primary definition including correct arguments

  2. applied only to a fall-through definition for those that did not match the primary definition

In my opinion the former has the advantage of less redundancy of the check patterns, while the latter has greater clarity and is easier to apply (or retrofit).

Fall-through method

Here is a simple example of the fall-through method. Both a General and user-created message are used for the sake of illustration. Note that to reduce redundancy I used no actual argument test in funcmsg but rather the problem is assumed based on the fact that the primary definition did not match. This may not be as robust as desired.

ClearAll[func, funcmsg]
Attributes[funcmsg] = {HoldAll};
func::"bad" = "Argument `1` is not a `2`.";

func[a : {__List}, i_: Automatic] := "operate"
func[___]?funcmsg := Null

funcmsg[f_[]] := Message[f::"argt", HoldForm[f], 0, 1, 2]
funcmsg[f_[_] | f_[_, _]] := Message[f::"bad", 1, "list of lists"]
funcmsg[x : f_[_, _, __]] := 
  Message[f::"argt", HoldForm[f], Length @ Unevaluated @ x, 1, 2]

Now any calls that do not match func[a : {__List}, i_: Automatic] (the primary definition) will be passed to the funcmsg function for messages to be issued. Examples, with echoed (returned) input expressly omitted:

func[]

func::argt: func called with 0 arguments; 1 or 2 arguments are expected. >>

func[1, 2]

func::bad: Argument 1 is not a list of lists.

func[{{1}, {2}}, 3]
"operate"

Catch-all method

A variation of the method above to apply the message function in all calls rather than those that fail. This is used by internal functions such as ArrayPlot, BarChart, Histogram and PolarPlot. Examples:

e : ArrayPlot[args___] /; checkArgsOptions[e, 0, 1, hiddenOptions[ArrayPlot]] := 
 With[{caller = ArrayPlot, 
   caught = Catch[ArrayPlotInternal[False, getArgs[args], getOpts[args]]]}, 
  caught /; caught =!= $Failed]


BarChart[a : PatternSequence[___, Except[_?OptionQ]] | PatternSequence[], 
   o : OptionsPattern[]]?ChartArgCheck := 
 With[{res = Catch[iBarChart[BarChart, a, o], "ParseNoData" | "ChartingError", $Failed]}, 
      res /; Head[Unevaluated[res]] =!= $Failed]


Histogram[a : PatternSequence[___, Except[_?OptionQ]] | PatternSequence[], 
   o : OptionsPattern[]]?HistArgCheck := 
 With[{res = Catch[iHistogramLayer1[a, o], "ChartingError", $Failed]}, 
      res /; Head[Unevaluated[res]] =!= $Failed]

The definition of ChartArgCheck used by BarChart looks like this:

Attributes[ChartArgCheck] = {HoldAll}

ChartArgCheck[b : f_[{}, opts : OptionsPattern[]]] := True

ChartArgCheck[b : f_[{{} ..}, opts : OptionsPattern[]]] := True

ChartArgCheck[b : f_[args___, opts : OptionsPattern[]]] := 
 Block[{len}, len = Length[Unevaluated[{args}]];
   If[len <= 1, ArgumentCountQ[f, len, 1, 1], 
    Message[f::"nonopt", Last[Function[z, "HoldForm"[z], HoldAll] /@ Unevaluated[{args}]],
      1, "HoldForm"[b]];
    False, False]] && optCheck[b]

Note that in the third definition a Message may be produced, and that it additionally calls optCheck which may also produce a Message from its definition:

Attributes[optCheck] = {HoldAll}

optCheck[b : f_[args___, opts : OptionsPattern[]]] := 
 Module[{bad, good}, good = Join[Options[f], HiddenOptions[f]];
  bad = FilterRules[{opts}, Except[good]];
  If[Length[bad] > 0, Message[f::"optx", First[bad], "HoldForm"[b]];
   False, True, True]]

Note that in each case (ArrayPlot, BarChart, Histogram) the arguments are passed to an inner function that does that actual processing. This often is necessary to catch all the argument patterns desired while also handling correct arguments properly.

Here is a simplified, self-contained example:

ClearAll[foo, bar, check]

Attributes[check] = {HoldAll};
foo::"bad" = "Argument `1` is not a `2`.";

foo[args___]?check := bar[args]
bar[a_, i_: Automatic] := "operate"

check[f_[]] := Message[f::"argt", HoldForm[f], 0, 1, 2]

check[f_[Except[{__List}], ___]] := Message[f::"bad", 1, "list of lists"]

check[x : f_[___]] := With[{argln = Length @ Unevaluated @ x},
   If[argln < 3, True, Message[f::"argt", HoldForm[f], argln, 1, 2]]
 ]
share|improve this answer
    
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
2  
@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. –  Mr.Wizard Apr 15 '13 at 21:02
1  
Ah, it keeps the input unevaluated. +1 –  rm -rf Apr 15 '13 at 21:06
    
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
show 5 more comments

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]

share|improve this answer
add comment

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.  
share|improve this answer
    
"too much", not "to much". –  J. M. Apr 18 '13 at 9:22
    
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. –  Mr.Wizard Apr 18 '13 at 9:26
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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