# Is it possible to combine a default value in a function definition with type-checking, in case the optional parameter is actually given in a call?

1.) In a function definition, e.g.

f[a_Real, b_Integer, c_:False]:=
Print["function f called with Real argument a=", a
, ", b=", b
, ", c=", c
];


the third parameter can be given a default value and it can be called with one less parameter. In the example the default is False. f remains unevaluated, if the first parameter does not have Head Real OR if the second one does not have Head Integer. But for the third one there is no type checking:

(* Tests... *)
f[1, 2, True]     (* remains unevaluated because first argument is not Real *)
f[1.0, 2, True]
f[2.0 ,2 ]
f[3.0, 3, 4]


All but the first test evaluate with the Print statement from the definition of f.

2.) Another way of checking parameters uses property tests whose names usually terminate with a capital Q, like BooleanQ, IntegerQ, LetterQ, ListQ, InexactNumberQ, StringQ and so on (there is nothing like RealQ). Before the test function a question mark has to follow the Underline pronounced “blank”:

ClearAll[a,b,c,g];
g[ a_?InexactNumberQ
, b_?IntegerQ
, c_?BooleanQ
] :=
Print["function g called with Real argument a=", a
, ", b=", b
, ", c=", c
];

(* Tests.... *)
g[1, 2, True]       (* remains unevaluated: 1 is no Inexact number *)
g[1.0, 2, True]     (* evaluates with the Print statement *)
g[2.0, 2]           (* remains unevaluated: too few arguments *)
g[3.0, 3, 4]        (* remains unevaluated: 4 is not True or False *)


BTW: Would RealQ[x_] := (Head[x] === Real); be the equivalent for real arguments, akin to IntegerQ for Integer arguments and BooleanQ for True/False? How does it differ from InexactNumberQ? If I ask Mathematica, it can’t decide whether RealQ as defined above and InexactNumberQ are the same thing, but for all examples I came up with, both returned the same result.

In the example for g, only the second test is evaluated because all arguments match their associated property tests. All others remain unevaluated because of a mismatch – as expected.

3.) Yet another way of checking parameters uses property tests or even more complicate test procedures and the construct /; after the underline, pronounced “provided that”. If one puts the parameter test conditions after the closing bracket of the parameter list, even relations between different parameters become possible: However, writing b_/.(b>a) inside of the parameter list does not work, because a is displayed blue or black instead of green, indicating no reference to parameters of the function but rather to another object (if it exists). The third alternative requires a bit more writing, but one can make more elaborate tests on the structure and content of the actual parameters:

ClearAll[a,b,c,h];
h[a_
, b_
, c_/;BooleanQ[c]           (* the test for c involves no other parameter, so it may be done here *)
]/;InexactNumberQ[a] && IntegerQ[b] && b>a := (* test involving a and b must be after the bracket *)
Print["function h called with Real argument a=", a
, ", b=", b
, ", c=", c
];

(* Tests... *)
h[1, 2, True]       (* remains unevaluated: 1st argument no Inexact number *)
h[1.0, 2, True]
h[2.0, 2, True]     (* remains unevaluated because  !b>a *)
h[2.0, 3]           (* remains unevaluated: too few arguments *)
h[3.0, 4, 5]        (* remains unevaluated: 4 is not True or False *)


In the tests, only in the second one, h, is evaluated because all arguments match their associated property tests. All others remain unevaluated. The result of the tests is:

h[1, 2, True]
function h called with Real argument a=1., b=2, c=True
h[2., 2, True]
h[2., 3]
h[3., 4, 5]


How would one modify my function definition giving a default value for an argument at the end of the argument list such that a given argument matches only if it matches BooleanQ in my example.

• For the sub question about InexactNumberQ you can use FindInstance[Not[InexactNumberQ[x]], x, Reals] Commented Dec 19, 2022 at 21:14
• Also 0.4 + 0.8*I // InexactNumberQ is True Commented Dec 19, 2022 at 21:16
• In the properties and relations section of NumberQ you can see that it is equivalent to MatchQ[#,_Integer|_Rational|_Real|_Complex]& and that excludes objects like Sin[1] with head Sin. Perhaps you would like NumericQ[#]&&Im[#]==0 & Commented Dec 19, 2022 at 21:22
• userrandrand, thank you for the counterexample showing that my RealQ is not the same as InexactNumberQ: I had forgotten to look in the complex plane! That's a good point! Commented Dec 20, 2022 at 12:07
• also FindInstance[Not[InexactNumberQ[x]], x, Reals] showed that rational numbers like -(12/5) are not InexactNumberQ and just checking the head you loose expressions like Sin[1] Commented Dec 20, 2022 at 12:10

f[a_Real,b_Integer,c:_?BooleanQ:False] := "match";

f[1,2,True]
(* f[1,2,True] *)

f[1.0,2,True]
(* "match" *)

f[2.0,2]
(* "match" *)

f[3.0,3,4]
(* f[3.0,3,4] *)

• I just found out that one could also modify my third example using c:False in the parameter list within the brackets and add &&BooleanQ[c] to the test conditions after the closing bracket /; and before :=. Commented Dec 19, 2022 at 21:09
• You could add that as a separate answer! Commented Dec 19, 2022 at 21:15

It is possible to modify my third example this way separating the test BooleanQ[c] from making the third parameter an optional one by assigning a default value inside the parameter list:

ClearAll[a,b,c,h];
[a_
, b_
, c_:False
/;InexactNumberQ[a] && IntegerQ[b] && b>a && BooleanQ[c]:= (* test involving both a and b must be after the bracket *)
Print["function h called with Real argument a=", a
, ", b=", b
, ", c=", c
];


The tests behave as described in the comments after them:

(* Tests... *)
h[1, 2, True]       (* remains unevaluated: 1st argument no Inexact number *)
h[1.0, 2, True]     (* evaluated *)
h[2.0, 2, True]     (* remains unevaluated because  !b>a *)
h[2.0, 3]           (* evaluated *)
h[3.0, 4, 5]        (* remains unevaluated: 4 is not True or False *)


I give this just for completeness. My example shows, that both methods: parameter tests inside the parameter list and the \; solution after it can be combined and \; seems to be advantageous to check for relations between different parameters, because after the closing bracket their names can be used in such relations.

The solution offered by user293787 however is easier to write.