# Attributes with Pattern test

Probably this question has no application but it helps understand basic issues about Attributes and functions.

I was writing a piece of code where I accidently defined a function like this:

ClearAll[f1]
SetAttributes[f1, HoldAll]
f1[list_List] := "evaluated"


When evaluated I get results as expected:

f1[{1, 2}]
(*"evaluated"*)
l={1,2};
f1[l]
(*f1[l]*)


But when I tried to check if this works with Pattern test I was surprised that evaluation happened in the function argument:

ClearAll[f2]
SetAttributes[f2, HoldAll]
f2[list_?(Head[#] == List &)] := "evaluated"
f2[{1, 2}]
(*"evaluated"*)
f2[l]
(*"evaluated"*)


Same result with Pattern Condition /;

My question is that what kind of behavior is this and why does not HoldAll hold the argument (which I understand is anything inside the brackets [] of the function) from evaluation and is this behavior same for all other Attributes?

Thank you.

• FYI this is discussed in the Possible Issues section of the PatternTest documentation Dec 29, 2015 at 22:38
• @SimonWoods Good to know, thanks. I wasn't aware that this has been discussed in the docs. Dec 29, 2015 at 22:52

### General explanation

The semantics of PatternTest and Condition is that the main evaluator is called to test the pattern, when those are used. It is called internally by the pattern-matcher, and this call is independent from the main evaluation procedure for the main expression. The reason is that the evaluations used to determine the pattern match should be considered sub-evaluations, rather than a part of the standard evaluation procedure for the main expression being evaluated.

This means that Hold* - attributes don't prevent evaluation requested by the pattern-matcher to test the fact of the match. This is one reason why one has to be careful when using such tests, to avoid evaluation leaks introduced through Condition and PatternTest via the pattern-matcher. In particular, you may want to use the construct like

f2[list_?(Function[Null, Head[Unevaluated @ #] == List, HoldAll])] := "evaluated"


to make it roughly equivalent to the first pattern you considered.

• Apparently, as noted by Simon Woods in comments, this issue is discussed in the Possible issues section of the PatternTest documentation page
• Clear but one more thing to make it fully clear. if the function is defined like this: f2[list_List?(Head[#] == List &)] := "evaluated" then no evaluation happened and the PatternTest is not able to call the evaluator . Dec 29, 2015 at 22:32
• @Algohi Yes, because _List acts as a prefiltering device. This means that the pattern-matcher stops at that point and does not need to call the test function, because it rejects the pattern at an earlier stage of _List. Dec 29, 2015 at 22:36
• @Algohi I'd rather say that for patterns of the type p_head /; test[p] or p_head?test, the procedure to match the pattern is that first, the p_head part is tested, and if the pattern doesn't match, the test from Condition or PatternTest is simply not performed. So yes, this is a particular form of short-circuiting, very sensible one IMO. I have discussed it in this answer. Dec 29, 2015 at 22:43