What is the reasoning behind making ForAll hold its arguments? I find this surprising and it causes behaviour that I found confusing. There are no other functions that I am aware of that deal with symbolic computation and hold their arguments.
The behaviour is documented:
The value of x in ForAll[x,expr] is taken to be localized, as in Block.
Here are some examples which I found confusing before realizing the HoldAll property:
In[94]:= Clear[x, z]
In[95]:= ForAll[x, x > -1, Abs[x] == x]
Out[95]= ForAll[x, x > -1, Abs[x] == x]
In[96]:= z = x
Out[96]= x
In[97]:= ForAll[z, x > -1, Abs[x] == x]
Out[97]= x > -1 \[Implies] Abs[x] == x
In[98]:= x = 1
Out[98]= 1
In[99]:= ForAll[x, x > -1, Abs[x] == x]
Out[99]= ForAll[x, x > -1, Abs[x] == x]
In[100]:= ForAll[z, x > -1, Abs[x] == x]
Out[100]= True
The two confusing points are that:
Even with z=x, ForAll[z, ...] behaved differently from ForAll[x, ...]. Of course this is because of the HoldAll. This bit me when I did something like ForAll[Variables[expr], expr > 0].
Now that we know that ForAll is HoldAll, we may assume that it is safe to use it with variables that have a value. Not so, because ForAll may evaluate to Implies, which is not HoldAll, like the majority of symbolic processing functions.
So is there anything special about ForAll (or Exists) that make it a good idea to have the HoldAll attribute, and wouldn't apply to functions such as Reduce, Solve, Integrate, etc.?