I could also replace _Integer by _?IntegerQ, with no noticeable difference. The problem is with _?NumberQ. I can't define the function using offset_Number - I have either to use offset_?NumberQ or offset_Real. Is one better than the other?

4 Answers
4

The differences between using _Integer and _?IntegerQ as to efficiency, robustness and cleanliness in my view are:

Efficiency

_Integer is more efficient. It belongs to what @LeonidShiffrin calls syntactic patterns and the evaluator doesn't need to be called to check if the pattern fits. On the other hand, ? and /; require a call to the evaluator

Robustness

Both have their issues. _?IntegerQ will leak evaluation in cases where you might not want to. For example

SetAttributes[f, HoldAll];
f[_?IntegerQ]:=8;

Now, evaluating f[Print@9] will print the number 9, and that wouldn't happen with _Integer. This isn't only an issue if your symbol has a holding attribute, because the user might always call the function using Unevaluated and expect it not to leak

_Integer, however, has the disadvantage of matching something like f[Integer[Plot[Sin[x], {x, 0, 10}]]] which is clearly not a good thing

The most robust solution I see is to fix IntegerQ using

f[_?(Function[i, IntegerQ@Unevaluated@i, HoldFirst])]:=8;

or to fix _Integer using

f[_Integer?(Function[i, AtomQ@Unevaluated@i, HoldFirst])]:=8;

If robustness is the priority, you probably can't avoid a call to the evaluator. It would be practical and neat to name a function

and then just use f[_Integer?holdAtomQ], f[_Real?holdAtomQ], etc, or f[_?(makeHolding[IntegerQ])]

Cleanliness

Personally I like _Integer more in this regard but I think this is totally up to you

As to your example, all that comes to mind is the reminder that NumberQ won't match numbers like Pi while NumericQ will.

Also, as a sidenote, graphics primitives use real coordinates, and if you give them exact coordinates they have to save both versions of the numbers, in case you want to recover them. If you are not interested in keeping the original exact values in the front end, it may make sense to convert the parameters to approximate reals whatever form they came in. Just an idea

A key issue, I think, is that PatternTest must evaluate its argument to work, even if function holds its arguments. Thus, there can be a huge difference in the timing between the two. Here's an example:

I think this question have gotten very good answers, but I just want to add my two cents to the topic.

f[_sheep] is a declarative typing, if something says it's a sheep, then it's a sheep, even if it doesn't make sense or will cause problems like: sheep[wolf[]]- Something which doesn't declare itself to be a Sheep isn't a Sheep even if you would suspect that it could perfectly well be assumed to be a sheep like wholly[sheep[]].

f[_?DuckQ] is duck typing. If it walks like a duck and quacks like a duck, then it's a duck. So for example if we define DuckQ[dog[]]=True then dog[] is a Duck, and can be given to any function assuming arguments of the type Duck[]

Declarative typing will be more efficient, since you can deduce everything from the heads of the arguments. Duck typing is more flexible, and allows you to do type checking without explicit declaring the type of everything. A good example is matrixQ. You could explicitly state that your data is a matrix and define

f[_matrix]:=....
f[matrix[{1,0,0},{0,1,0},{0,0,1}]]

But most consider it nicer to just write out the structure as a list of lists, and use duck typing to check if it's a matrix.

An additional element of this is that declaritive typing as implimented in Mathematica does not allow type hiarchies, so You cannot define your function to accept animals f[_animal]:=... and expect it to work for sheep[] and duck[] but this can be handled easily by an appropriate "duck typed" definition f[_?animalQ]:=... with animalQ[_sheep]=True;animalQ[_duck]=True. Again showing the flexibility of defining boolean checks.

Mathematica is a registered trademark of Wolfram Research, Inc. While the mark is used herein with the limited permission of Wolfram Research, Stack Exchange and this site disclaim all affiliation therewith.