0
$\begingroup$

I'm trying to use Block to prevent a function from executing. In this example, the function I'm trying to block will simply be Plus. My code has an inner With clause inside the Block. The With clause performs some operations itself, and also receives an expression passed-in as a function parameter. In the below example, I emulate that with an outer With clause.

Unexpectedly, for a simple two-operand Plus, Block is only blocking Plus for the expression passed-in to the inner With clause, not the Plus operation hard-coded into the With clause. For a three-operand Plus, it blocks everything.

A canonical code sample which produces this behavior is shown below. Any ideas? I may switch to using Inactivate for my real problem, though I have to think it through a bit more. Thanks!

EDIT: Changed example to Print the results instead of Hold'ing them, as the Hold[#]& @ ... syntax required to illustrate the "extent of execution within the Block" was obfuscating my intent.

With[{a := 1 + 2},
 Block[{Plus}, SetAttributes[{Plus}, HoldAllComplete];
  With[{x := {a + 3 + 4, a*2*2}, y := {a + 3 + (4 + 5), a*2*2}},
   Print["x: ", x];
   Print["Unevaluated@x: ", Unevaluated@x];
   Print["y: ", y];
   Print["Unevaluated@y: ", Unevaluated@y];
   ]]]

Results:

x: {7+(1+2),12}
Unevaluated@x: {(1+2)+3+4,(1+2)*2*2}
y: {3+(1+2)+(4+5),12}
Unevaluated@y: {(1+2)+3+(4+5),(1+2)*2*2}

EDIT:

Expected results:

x: {(3+4)+(1+2),(1+2)*4} (* DELTA *)
Unevaluated@x: {(1+2)+3+4,(1+2)*2}
y: {3+(1+2)+(4+5),(1+2)*4} (* DELTA *)
Unevaluated@y: {(1+2)+3+(4+5),(1+2)*2}

My real code has to do with code injection into held expressions.

EDIT: Another simplified example (Thanks @PierreALBARÈDE):

Block[{Plus}, Attributes[Plus] = {HoldAllComplete}; 
 Print /@ {Plus[1, 2], Plus[1, 2, 3], Plus[1 + 2, 0], 
   Plus[1 + 2 + 3, 0]};]

Results:

3
6
1+2
1+2+3

It seems that only the HoldAllComplete attribute is doing anything...

$\endgroup$
11
  • 2
    $\begingroup$ Apparently you are not happy with the results. Could you add a column of expected results to make the question more clear? $\endgroup$ Jan 31, 2021 at 18:58
  • $\begingroup$ @PierreALBARÈDE : Added $\endgroup$
    – Sean
    Jan 31, 2021 at 19:15
  • 3
    $\begingroup$ Keep in mind that according to the docs Plus and Times have internal rules that are applied before the standard evaluation cycle. You cannot override them. It does not say what they are exactly, so they may or may not be the cause of the trouble. $\endgroup$
    – Michael E2
    Jan 31, 2021 at 20:03
  • $\begingroup$ Ahh @MichaelE2, that would be good news, maybe my test code just happened to hit one of those cases. It's odd that using Inactivate[expr,Plus] instead of Block[{Plus},expr] behave differently, and that the two-operand vs three-operand versions of Plus behave differently, but like you say... it could be some oddity of the internal rules. $\endgroup$
    – Sean
    Jan 31, 2021 at 20:08
  • 1
    $\begingroup$ When I see a question about the behavior of Plus, where someone is attempting to change it using Unprotect or Block, I always think "No. Don't do that. Find another way to get what you need." It's just not an approach that can be made to work reliably. $\endgroup$ Feb 1, 2021 at 1:25

1 Answer 1

1
$\begingroup$

You are running afoul of self-created complications when using the unnecessary Hold[#]&@... form, instead of Hold@... or Hold[...]:

With[
 {a := 1 + 2},
 Block[{Plus},
  SetAttributes[{Plus}, HoldAllComplete];
  With[
   {x := a + 3 + 4, y := a + 3 + (4 + 5)},
   Hold@{x, Unevaluated@x, Hold@x, y, Unevaluated@y, Hold@y}]
 ]
]

(* Out: 
Hold[{
  (1 + 2) + 3 + 4, 
  Unevaluated[(1 + 2) + 3 + 4], 
  Hold[(1 + 2) + 3 + 4], 
  (1 + 2) + 3 + (4 + 5), 
  Unevaluated[(1 + 2) + 3 + (4 + 5)],
  Hold[(1 + 2) + 3 + (4 + 5)]
}]
*)

Although I don't know what your ultimate goal is here, nevertheless this seems consistent.

$\endgroup$
17
  • $\begingroup$ Thanks @MarcoB. I added some clarification on my use of Hold[#]&@... above. My ultimate goal has to do with code injection into held expressions, so I actually want to let everything except the Plus (in this simple example) pre-execute before holding anything. $\endgroup$
    – Sean
    Jan 31, 2021 at 19:18
  • $\begingroup$ Perhaps I could have used a less-trivial (but less canonical) bit of sample code, but wouldn't you agree that the as-written Hold form should execute the Hold before leaving the Block, therefore producing my expected results of Hold[(1+2)+(3+4)] for the first output line? $\endgroup$
    – Sean
    Jan 31, 2021 at 19:21
  • 1
    $\begingroup$ I am not sure that I understand. Hold won't have any effects "after the fact": in other words, once an expression is evaluated, applying Hold to it won't do much anymore. Compare Hold[(3 + 2)] (returning Hold[3 + 2]) with Hold[#] &@(3 + 2) (returning Hold[5]). The latter Hold was "too late" and had no chance of affecting anything. $\endgroup$
    – MarcoB
    Jan 31, 2021 at 19:24
  • 1
    $\begingroup$ @Sean In your Block, Plus is not inert. See Block[{Plus}, SetAttributes[{Plus}, HoldAllComplete]; Plus[3, 2]] which returns 5. What do you mean then? $\endgroup$
    – MarcoB
    Jan 31, 2021 at 19:36
  • 1
    $\begingroup$ Even simpler try this: Block[{Plus}, Attributes[Plus] = {HoldAllComplete}; Print /@ {Block[{Plus}, Attributes[Plus] = {HoldAllComplete}; Print /@ {Plus[1, 2], Plus[1 + 2, 3, 4], Plus[1 + 2, 3, 4, 5], Plus[1 + 2, 3, 4 + 5]};] Plus[1, 2], Plus[1 + 2, 3, 4], Plus[1 + 2, 3, 4, 5], Plus[1 + 2, 3, 4 + 5]};] Then you see that only the outermost Plus is evaluated. $\endgroup$ Jan 31, 2021 at 20:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.