1
$\begingroup$

I've got a weird problem here....

So I have a function that takes blocks of codes and them uses Map on them and passes them around and then selects and returns the one that is needed.

A simplified dummy of it is given below:

In:
SetAttributes[func, HoldAll];
func[exprs__] := Which[Evaluate[Sequence @@ (# & /@ Inactivate[{exprs}])]];

Now, using it:

(* say every even-numbered index argument is a code block: *)
In: func[a, 1, b, x = 1; 2, c, 0]
Out: Which[a, 1, b, x = 1; 2, c, 0]

In: Simplify[%, a == False && b == True]
Out: x = 1; 2

(* along the line it gets activated *)
In: Activate[%]
Out: 2

In: x
Out: 1

Ok, now what if x has been set: x = 9 before the code immediately block-quoted above.

In: func[a, 1, b, x = 1; 2, c, 0]
Out: Which[a, 1, b, 9 = 1; 2, c, 0]

So by the time it gets to Activate, we have a: Set::setraw error

The Inactivate and SetAttributes[_, HoldAll] makes sure the x does not immediately gets aggressively replaced by 9, but somewhere along the line, the Map, Sequence and Evaluate aggressively replaces it. I haven't tried SetAttribute-ing them all for HoldAll, but is less intrusive way (from the point-of-view of these built-in functions) of holding these replacements??


In reality, my func immediately calls Activate, looking more like this very simplified dummy:

In: func[exprs__] := Activate[Which[Evaluate[Sequence @@ (# & /@ Inactivate[{exprs}])]];

But the step-by-step code further above that I've shown re-produces what I've been getting. Also with immediately above:

In: func[a, 1, b, x = 1; 2, c, 0]
Out: Which[a, 1, b, 9 = 1; 2, c, 0]

In: Simplify[%, a == False && b == True]
Out: 2

and the 9 = 1; magically doesn't seem to happen (neither x is 1 nor a setraw error pops up), so I decided it was a poor example to state the case. But the question is the same, is there a less intrusive way?

$\endgroup$
  • $\begingroup$ C'mon.... It should just be a combination of Unevaluated, Inactivate or something... I just don't wanna be blindly cracking it. $\endgroup$ – kozner Nov 30 '16 at 7:01
  • $\begingroup$ Possibly related: mathematica.stackexchange.com/q/88018/121 $\endgroup$ – Mr.Wizard Nov 30 '16 at 7:32
2
$\begingroup$

I am not quite following the underlying purpose of this code but here a few thoughts.

  1. I believe you can implement the function you wrote more simply as

    SetAttributes[func, HoldAll];
    
    func[exprs__] := Which @@ Inactivate[{exprs}];
    
  2. The evaluation of x comes from the fact that Inactivate does not hold the arguments of the expressions that it inactives, only the heads. More explicitly the long form of x = 1 is Set[x, 1] which is transformed to Inactive[Set][x, 1] and the compound head Inactive[Set] does not have a Hold attribute.

To propose a solution I need to understand how you are using this function and how or where your Set operations appear generally, rather than in a single example.

As a point of reference your dummy operation would appear to reduce to

SetAttributes[func, HoldAll];

func[exprs__] := Which[exprs];

But I assume you actually want to perform other manipulations on exprs before inserting them into Which. You may want Hold and ReleaseHold rather than Inactivate and Activate.

Another guess at what you might want would be something like this:

SetAttributes[func, HoldAll];

func[exprs__] := foo /@ Hold /@ Unevaluated[{exprs}];

func[a, 1, b, x = 1; 2, c, 0]
  {foo[Hold[a]],
   foo[Hold[1]],
   foo[Hold[b]],
   foo[Hold[x = 1; 2]], 
   foo[Hold[c]],
   foo[Hold[0]]}
$\endgroup$
  • $\begingroup$ For your (1), yes, I'm aware that you can simply func[exprs__] := Which @@ Inactivate[{exprs}]; or func[exprs__] := Which[exprs]; but I'm replicating the process that my actual library is going through and possiblity where the semicolon-ed code are being evaluated too soon. As I stated "but somewhere along the line, the Map, Sequence and Evaluate aggressively replaces it" $\endgroup$ – kozner Nov 30 '16 at 7:57
  • $\begingroup$ As for func[exprs__] := foo /@ Hold /@ Unevaluated[{exprs}];... That's interesting, I didn't know that shorthand, I would've done Hold[foo] & /@ Unevaluated[{exprs}] $\endgroup$ – kozner Nov 30 '16 at 7:59
  • $\begingroup$ Alright, I'll try Hold-ing every argument $\endgroup$ – kozner Nov 30 '16 at 8:01
  • $\begingroup$ I've tried your # & /@ Hold /@ Unevaluated[{exprs}], it's different from the usual Hold[#] & /@ Unevaluated[{exprs}]... It really passes Hold first without evaluating the # at all. $\endgroup$ – kozner Nov 30 '16 at 8:24
  • $\begingroup$ Where did you get the reference for # & /@ Hold /@ Unevaluated[{exprs}]?? Map according to the documentation: "Map always effectively constructs a complete new expression and then evaluates it." Meaning, this is preorder passing # rather than inorder passing, as that is the only explanation that the # doesn't get to evaluate raw. $\endgroup$ – kozner Nov 30 '16 at 9:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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