2
$\begingroup$

Consider this function

w[x_Integer: 2, y__List : {1, 2}, z_String: "X"] := {x y, z}
w[4]

{{4, 8}, "X"}

Now I want to define an error message to be displayed in case of a wrong entry. The way I know is

w::argerr = "error with `1`, `2`, `3`";
w[x1___, x2___, x3___] := (Message[w::argerr, x1, x2, x3]; $Failed);

which only works well when all the arguments are provided. For example

w[x]

StringForm::sfr: Item 2 requested in "error with 1, 2, 3" out of range; 1 items available.

StringForm::sfr: Item 3 requested in "error with 1, 2, 3" out of range; 1 items available.

w::argerr: error with l, 2, 3

$Failed

How to construct a proper error message when one of the entry is wrong and others are supposed to take default values. For example the error message I am looking for

w[x]

error with x

w[4,2]

error with 2

$\endgroup$
3
  • $\begingroup$ I don't quite get what you're after. Do you want to handle insert the default values of the other parameters in the error message or do you want to handle the case of missing arguments differently from the case of wrong ones? $\endgroup$
    – Lukas Lang
    Commented Jun 11, 2018 at 14:40
  • $\begingroup$ Some construct like w[x_: 2, y_: {1, 2}, z_: "X"] := If[! IntegerQ[x], Message[w::argerr, x, y, z]; $Failed, True] displays the error message correctly using the default values for inputs like w[x], if that's what you were going for? $\endgroup$ Commented Jun 11, 2018 at 14:51
  • $\begingroup$ The way you wrote in your post doesn't seem to be suitable because you would be defining a different downvalue. Even if you could access other downvalues, there still are some problems because Mathematica would try to match the input in three different ways: x as first, second, and third argument, as seen in something like ReplaceList[{x}, {a_: 1, b_: 2, c_: 3} :> {a, b, c}]. This would get really messy if you were to deal with second/third arguments. $\endgroup$ Commented Jun 11, 2018 at 15:02

2 Answers 2

2
$\begingroup$

I don't think there is a nice solution that does exactly what you're asking, as it is not really possible to give a well-define behavior: Since x doesn't match any of the argument patters, which argument should it be? The first one? Or maybe it looks more like the second one? This will probably lead to messages that are very hard to understand.

For this reason, I propose the following:

w[x_Integer: 2, y__List : {1, 2}, z_String: "X"] := {x y, z}
w::argerr = "Error in ``. Arguments are of incorrect type";
call : w[___] := (Message[w::argerr, HoldForm@call]; $Failed);

w[4]
(* {{4, 8}, "X"} *)

w[x]

(* w::argerr: Error in w[x]. Arguments are of incorrect type *)
(* $Failed *)

This has several advantages:

  • It allows you to handle any form of wrong arguments: too few, too many, wrong types
  • It is easy to maintain - no need to update defaults or conditions in multiple places
  • It provides very clear message for the user: It shows exactly which call caused the error, without any defaults inserted (which might make it difficult to identify where the call came from in longer code)
$\endgroup$
0
$\begingroup$

This code seems to work fine with the example you posted, but it returns $Aborted when the argument check is not passed. Usually one wants to return unevaluated input, but unfortunately I don't know how to do it in this case.

ClearAll[w]
w::argerr = "the argument `1` is incorrect";
w[x_Integer: 2, y_List: {1, 2}, z_String: "X"] := {x*y, z};
w[x_: 2, y_: {1, 2}, z_: "X"] /; (Message[w::argerr, Which[
      Not[IntegerQ[x]], HoldForm[x],
      Not[ListQ[y]], HoldForm[y],
      Not[StringQ[z]], HoldForm[z]]]; Abort[]) := Null;

Now try how it works:

In[295]:= w[r]

During evaluation of In[295]:= w::argerr: the argument r is incorrect
Out[295]= $Aborted

In[296]:= w[1, XX]

During evaluation of In[296]:= w::argerr: the argument XX is incorrect
Out[296]= $Aborted

In[298]:= w[4, 2, "t"]

During evaluation of In[298]:= w::argerr: the argument 2 is incorrect
Out[298]= $Aborted

It gives only one message at a time, like built-in functions (at least most of them):

In[299]:= w[1, 2, 12]

During evaluation of In[299]:= w::argerr: the argument 2 is incorrect
Out[299]= $Aborted

You can also look at this question for more ideas.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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