8

It is customary to use the following idiom to partially evaluate something inside a held expression:

f[x_] := 2 x
Block[{f}, Hold[f[1 + 1]] /. x_f :> With[{r = x}, r /; True]]

Out[]= Hold[f[2]]

This works also with built-in symbols, like

Block[{Abs}, Hold[Abs[1 + 1]] /. x_Abs :> With[{r = x}, r /; True]]

Out[]= Hold[Abs[2]]

However, I cannot understand the behaviour of the following

Block[{Plus}, Hold[1 + 2*2] /. x_Plus :> With[{r = x}, r /; True]]

which returns Hold[5] instead of Hold[1+4]. Can someone explain why?

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
Federico
  • 2,553
  • 16
  • 16
  • 4
    Times has the same issue, so I suspect they're wired into the parser at a deep lvl in the name of efficiency. – rcollyer Aug 03 '15 at 18:24
  • 1
    Related: http://mathematica.stackexchange.com/questions/3261/block-attributes-of-equal – Szabolcs Aug 03 '15 at 18:51
  • 1
    I believe this is the reason: http://mathematica.stackexchange.com/questions/19126/why-does-this-pattern-with-plus-not-work-for-numbers/19129#19129 – Michael E2 Aug 04 '15 at 00:59

2 Answers2

7

I think my answer to Why does this pattern with Plus not work for numbers? is also the answer here.


See Plus in the reference manual:

Unlike other functions, Plus applies built-in rules before user-defined ones. As a result, it is not possible to make definitions such as 2+2=5.

The ability for user-defined rules to supersede built-in ones was lost in Version 3.


There is a similar property for Times.

Here is some evidence that rules for Plus are applied internally even when Plus is blocked:

Block[{Plus}, 1 + 2*2 /. Plus -> List]
(*  5  *)

p[x__] := Plus[x];
Block[{p}, p[1, 2*2] /. p -> List]
(*  {1, 4}  *)

Update -- some more evidence:

Block[{Plus}, 
  Hold[1 + 2*2] /. x_Plus :> With[{r = x}, r /; True]] // Trace

Mathematica graphics

Block[{p}, 
  Hold[p[1, 2*2]] /. x_p :> With[{r = x}, r /; True]] // Trace

Mathematica graphics

Michael E2
  • 235,386
  • 17
  • 334
  • 747
  • Michael, I marked this question as a duplicate. Please let me know if you disagree. Also I suspect you meant to link to 19126 rather than 3261 when you wrote "my answer to ..." – Mr.Wizard Aug 04 '15 at 01:40
  • @Mr.Wizard I don't disagree. I considered proposing it, but I wasn't sure I had nailed the reason. -- And thanks, for pointing out I had copied the wrong link! – Michael E2 Aug 04 '15 at 01:43
  • It occurs to me that I have a work-around that is applicable here but not in the original as marked. Do you think I should reopen for that or just append it here in passing? – Mr.Wizard Aug 04 '15 at 01:49
  • @Mr.Wizard If it were me with such a work-around, I would probably nominate for re-opening. (I have done so in the past.) But it's up to you. – Michael E2 Aug 04 '15 at 01:54
  • I posted. Let me know what you think or if you find problems with it, please. – Mr.Wizard Aug 04 '15 at 05:46
3

I closed this as a duplicate of Why does this pattern with Plus not work for numbers? but I think I have something that is general enough to be useful, and it's not applicable to that question.

We may observe that although a plain use of Block[{Plus}, . . .] does not prevent numeric evaluation of Plus we can still make a substitution that does:

Block[{Plus = plus}, Hold @@ {2 + 2}]
Hold[plus[2, 2]]

From there we could replace plus with Plus again. Automating this process:

Attributes[hardBlock] = {HoldAll};

hardBlock[sym : {___Symbol}, body_] :=
  Block[sym, sym = #; body] /. Thread[# :> sym] & @ Module[sym, sym]

Now:

expr := Hold @@ {1 + 2 + 3*4}

Block[{Plus, Times}, expr]   (* original Block behavior for reference *)

hardBlock[{Plus},        expr]
hardBlock[{Times},       expr]
hardBlock[{Plus, Times}, expr]
Hold[15]

Hold[1 + 2 + 12] Hold[3 + 3 4] Hold[1 + 2 + 3 4]

And for completeness:

hardBlock[{Plus}, Hold[1 + 2*2] /. x_Plus :> With[{r = x}, r /; True]]
Hold[1 + 2 2]
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • 1
    Have you looked at hardBlock[{Plus}, Hold[1 + 2*2] /. x_Plus :> With[{r = x}, r /; True]] // Trace? I think the Plus inside Hold is neither changed nor replaced. Consider hardBlock[{Plus}, Hold[1 + 2*2] /. HoldPattern[x_Plus] :> With[{r = x}, r /; True]] instead. (The thing that made me wonder was that 2*2 did not become 4.) – Michael E2 Aug 04 '15 at 11:13
  • @Michael Thank you! I worried there would be problems like that I had failed to think through. I'll have to consider how this case might be improved, or if this construct still has a purpose. :-/ – Mr.Wizard Aug 04 '15 at 18:26