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$
2
  • $\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
    Commented Nov 30, 2016 at 7:01
  • $\begingroup$ Possibly related: mathematica.stackexchange.com/q/88018/121 $\endgroup$
    – Mr.Wizard
    Commented Nov 30, 2016 at 7:32

1 Answer 1

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$
7
  • $\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
    Commented Nov 30, 2016 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
    Commented Nov 30, 2016 at 7:59
  • $\begingroup$ Alright, I'll try Hold-ing every argument $\endgroup$
    – kozner
    Commented Nov 30, 2016 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
    Commented Nov 30, 2016 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
    Commented Nov 30, 2016 at 9:42

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.