you are good! Based on your comments, I crafted this:
Block[{Equal, H = Developer`ToPackedArray},
SetAttributes[Equal, Listable];
Equal[x_H, y_H] :=
Equal[Developer`FromPackedArray[x], Developer`FromPackedArray[x]];
a == b]
which works in both cases.
Update
Mr.Wizard said " I expected symbols localized with Block to behave generically." Although the above works, as mentioned in the comments, there is an observable difference between Equal and other user defined symbols in the rewrite/eval loop in regards to Listable and automatic unpacking of packed arrays. I played with different definitions and couldn't find why this happens.
Clear[f, g, h, z1, z2];
ClearAttributes[{z1, z2}, {Listable}];
f[a_, b_] := Block[{Equal, H = Developer`ToPackedArray},
SetAttributes[Equal, Listable];
Equal[x_H, y_H] :=
Equal[Developer`FromPackedArray[x],
Developer`FromPackedArray[x]];
Print[ a == b // FullForm];
a == b];
g[a_, b_] := Block[{Equal, H = Developer`ToPackedArray},
SetAttributes[Equal, Listable];
Print[ a == b // FullForm];
a == b];
h[a_, b_] := Block[{Equal = z1, H = Developer`ToPackedArray},
SetAttributes[z1, Listable];
Print[ a == b // FullForm];
a == b] /. z1 -> Equal;
i[a_, b_] := Block[{Equal = z2, H = Developer`ToPackedArray},
SetAttributes[Equal, Listable];
Print[ a == b // FullForm];
a == b] /. z2 -> Equal;
Then I did
{a , b } = {{1, 2}, {1, 3}};
{c , d} = Developer`ToPackedArray /@ {a, b};
which produces,
In[211]:= f[a, b]
List[Equal[1,1],Equal[2,3]]
Out[211]= {True, False}
In[212]:= f[a, c]
List[Equal[1,1],Equal[2,2]]
Out[212]= {True, True}
In[213]:= g[a, b]
List[Equal[1,1],Equal[2,3]]
Out[213]= {True, False}
In[214]:= g[a, c]
Equal[List[1,2],List[1,2]]
Out[214]= True
In[215]:= h[a, b]
List[z1[1,1],z1[2,3]]
Out[215]= {True, False}
In[216]:= h[a, c]
List[z1[1,1],z1[2,2]]
Out[216]= {True, True}
In[217]:= i[a, b]
z2[List[1,2],List[1,3]]
Out[217]= False
In[218]:= i[a, c]
z2[List[1,2],List[1,2]]
Out[218]= True
So one can see that there is a difference between g[a,c] and h[a,c]: in g Equal does not unpack, whereas in h the user-defined z1 does. I think all the other behaviours can be explained from the evaluation (rewrite) steps as explained in the Mathematica documentation.
Anyway, just wanted to comment finally that although Mr Wizard's is a fair claim. There are a number of areas where other than pure symbolic/rewrite manipulation is occurring, and that simply Block is not probably considering. For example -hope not to trivial for you-, Block[{Equal}, ToExpression["?Equal"]] still prints the Equal documentation, instead of a reference to an undefined symbol. So, like in this case, maybe Equal (and other built-ins) have special behaviour which Block is not touching.
Sorry, I will leave it as answer, but now probably I should say it is not...
Update 2
Actually, just checked that Block leaves ::usage untouched! So, if you do
f::usage = "Symbol f";
then
Information[f]
Symbol f
And if you do Block still you get the same
Block[{f}, Information[f]]
Symbol f
so Block and Information are not working together, even for user-defined symbols!
Last update
As noticed by some, symbols like Plus have special behaviour too. So this
Block[{Plus}, Print[Trace[Plus[1, 2]]]]; (* 1 *)
Block[{Global`Plus}, Print[Trace[Plus[1, 2]]]]; (* 2 *)
Block[{Global`Plus}, Print[Trace[Global`Plus[1, 2]]]]; (* 3 *)
produces
{1+2,3}
{1+2,3}
{}
which demonstrates that Plus retains the built-in behaviour in 1 and 2, being really overridden only with a syntax like 3, which is not convenient. By the way, 2 generates a warning.
Bottom, line, to answer Mr.Wizard's request -I believe this answer was suggested in the comments- it seems the only generic way to override a built-in is to provide your own user defined symbol instead. If one wants to redefine, say Plus, IMHO it is not a burden as whatever definition one wants to introduce can be done with the user-defined symbol and still one has the convenience of the syntactic sugar. To wit
Block[{Plus = plus}, plus[0, _] := 0; Print[Trace[0 + 2]]];
which produces
{{Plus,plus},plus[0,2],0}
So, I will leave it like this.
Equal(and perhaps some other operators) are internally overloaded on packed array arguments for speed, in a way incompatible with the main evaluation sequence. I suspect that this was hard-wired to the system, so that such redefinitions are not even expressed as internal rules (amenable toBlock). But these are all just guesses. – Leonid Shifrin Mar 20 '12 at 18:11Block[{Equal},SetAttributes[Equal, Listable];Equal[a_,b_]:=Hold[Equal[a,b]];A==B]//ReleaseHoldgives{{{False, False}, {False, True}, {False, True}}, {{False, False}, {False, False}, {True, False}}, {{False, True}, {False, False}, {False, False}}, {{False, True}, {False, False}, {False, True}}}– celtschk Apr 10 '12 at 10:24Traceon your expression v. Mr. Wizard's shows that by redefiningEqual, as you do, it is actually threaded over the lists, yet Mr. Wizard's is not. UsingSetSystemOptions["PackedArrayOptions" -> "UnpackMessage" -> True]reveals that in both cases some unpacking does occur, but in Mr. W's case we get "Unpacking array with dimensions {1}" v. "Unpacking array with dimensions {4,3,2} in call to Equal" in your case. By redefiningEqualit automatically unpacks the full array. – rcollyer Apr 11 '12 at 17:21Thread,Outer,Flatten, whatever) associated with it seems to have this behaviour. I tested usingWith[{f = Equal}, Block[{f}, SetAttributes[f, Listable]; With[{r = f[a, b]}, Hold[r]]]]. This property of builtins can be removed by issuing any user-definition (as @celtschk showed above). Another common "unusual" behaviour of builtins is that their definitions take effect after any user-made definitions. – Szabolcs Apr 12 '12 at 15:37Listablefunction. Which builtin isListablethat is also optimized for packed arrays I am not sure, I did not find it (I didn't look much either). But if we find one, that could be a support for this hypothesis. – Szabolcs Apr 12 '12 at 15:39Blockdoesn't work as expected with built-in operations:Block[{Plus},Hold@Evaluate[1+1]]givesHold[2]instead ofHold[1+1](writingPlus[1,1]instead of1+1doesn't change that result). That one cannot even be circumvented with an user-definition:Block[{Plus},Plus[a_,b_]:=plus[a,b];Hold@Evaluate[1+1]]still givesHold[2], notHold[plus[1,1]]. – celtschk May 11 '12 at 13:36Block[{Plus = z}, ToString[1 + 1]]returns"z[1, 1]"-- I guess a lesson in this is that plainBlock[{x}, ...]doesn't really clear everything it should, but giving something to replace it with ({x = ...}) helps. – Mr.Wizard May 11 '12 at 21:10Block[{Plus},Plus=z;ToString[1+1]]also gives"z[1, 1]". Therefore the difference seems to be betweenOwnValuesandDownValues. – celtschk May 12 '12 at 11:31A==Breturns (False) for both non-packed and packed arrays. – Ronald Monson Oct 06 '20 at 17:32