Quick way to use conditioned patterns when defining multi-argument function?

Ramblings

Arguments of the left-hand-side head are evaluated in the course of function definition, therefore you can use a utility function that constructs the patterns that you want. For example:

SetAttributes[nq, HoldFirst]
Quiet[
 nq[s_Symbol] := s_?NumericQ
]

Now:

ClearAll[f]

f[nq @ a, nq @ b, nq @ c] := a + b + c

Definition[f]
f[a_?NumericQ, b_?NumericQ, c_?NumericQ] := a + b + c

Doing this you lose the nice syntax highlighting shown in the original.

If you Block all Symbols you could even Map the utility function, e.g.:

Block[{f, a, b, c},
  Evaluate[nq /@ f[a, b, c]] := a + b + c
]

This hardly feels like a clean solution however. Perhaps you merely want something shorter than verbatim NumericQ? At risk of a tautology you could always do something like:

ClearAll[f]

q = NumericQ;
f[a_?q, b_?q, c_?q] := a + b + c

But this requires you to keep the Global definition q or it will break as it is not expanded to NumericQ in the definition itself:

Definition[f]
f[a_?q, b_?q, c_?q] := a + b + c

Metaprogramming approach

Another approach would be to write a function to modify all Pattern objects on the left-hand-side at the time of assignment. Something like:

SetAttributes[defWithTest, HoldFirst]

defWithTest[(s : Set | SetDelayed)[LHS_, RHS_], test_] := 
  s @@ Join[Hold[LHS] /. p_Pattern :> p?test, Hold[RHS]]

Now:

ClearAll[f]

defWithTest[
  f[a_, b_, c_] := a + b + c,
  NumericQ
]

Definition[f]
f[a_?NumericQ, b_?NumericQ, c_?NumericQ] := a + b + c

Proposed solution

As Kuba and rasher show in the comments you could also use clever alternatives to the explicit form f[a_?NumericQ, b_?NumericQ, c_?NumericQ]. Inspired by those comments I propose:

SetAttributes[numArgsQ, HoldFirst]
numArgsQ[_[___?NumericQ]] := True

Now:

ClearAll[f]

f[a_, b_, c_]?numArgsQ := a + b + c

Test:

f[1, 2, 3]
f["a", 2, 3]
6

f["a", 2, 3]

For examples of advanced argument testing with an emphasis on messages please see:

  • How to check the style and number of arguments like the built-in functions?

Note how (some) internal functions pass additional argument checking (and message generation) to an auxiliary function, e.g. ChartArgCheck, much as I did in the minimal application of numArgsQ above.


Update

I was reading the comments to the question, and found that @Kuba had already provided the following answer. I think it's the cleanest solution, so it deserves to be an answer, but please credit him with the idea.

Another idea is to use PatternSequence:

ClearAll[f]

f[PatternSequence[a_,b_,c_]?NumericQ] := a+b+c

Examples:

f[1,2,3]
f["a",2,3]

6

f["a", 2, 3]

Generalization

We can generalize the idea behind this answer as follows:

SequencedPattern[a_] := PatternSequence[##]?a&

Then, we can use SequencedPattern as follows:

f[SequencedPattern[NumericQ][a_, b_, c_]] := a + b + c

The previous examples still work:

f[1, 2, 3]
f["a", 2, 3]

6

f["a", 2, 3]

Here is another example using SequencedPattern:

f[SequencedPattern[PrimeQ][a_, b_, c_], d_] := (a+b+c)/d

f[2,3,4,10]
f[2,3,7,11]

f[2, 3, 4, 10]

12/11


I think there is another way to do that:

f[s : (PatternSequence[_?NumericQ] ..)] /; Length[{s}] == 3 :=
 Function[{a, b, c}, a^2 Sin[b] Log[c]][s]

It's suitable for the cases that conditions are the same.