Evaluation process in Mathematica can be tricky at the beginning; I suggest reading the tutorial on the Evaluation of Expressions.
The problem you are facing is that both With
and If
hold their arguments (With
is HoldAll
, and If
is HoldRest
). In order to circumvent this, you can wrap the argument with Evaluate
.
Let's begin with simpler examples:
cond = a > 0;
With[{a = 1}, cond]
(* a > 0 *)
Why did With
not change the value of a
? Because, roughly speaking, With
replaces only symbols it can lexically explicitly "see"! But it only sees cond
, because the evaluation of cond
to a > 0
is postponed due to HoldAll
. If you cirvument this, cond
is evaluated before With
does its magic:
With[{a = 1}, Evaluate[cond]]
(* True *)
You can more closely inspect all of this by using Trace
.
With[{a = 1}, cond] // Trace
(* {With[{a=1}, cond], cond, a>0} *)
With[{a = 1}, Evaluate[cond]] // Trace
(* {{cond, a>0}, With[{a=1},a>0], 1>0, True} *)
See the difference?
Now to adding If
:
int = a x;
With[{a = 1}, If[cond, int]]
(* If[a>0,int] *)
With[{a = 1}, Evaluate[If[cond, int]]]
(* a x *)
The first output is expected: With
didn't see any a
to be replaced, so the output is an undecided If
. But what's with the second output? Look at the Trace
:
With[{a = 1}, Evaluate[If[cond, int]]] // Trace
(* {{{cond,a>0},If[a>0,int]},With[{a=1},If[a>0,int]],If[1>0,int],
{1>0,True},If[True,int],int,a x} *)
Because If
holds its arguments (except the first one), they get evaluated only after With
has done the replacement! So you have to now also circumvent this holding inside If
with another Evaluate
:
With[{a = 1}, Evaluate[If[cond, Evaluate[int]]]]
(* x *)
Now let's add Print
s:
With[{a = 1},
Evaluate[
If[cond,
Evaluate[Print["1st"]; int],
Evaluate[Print["2nd"]; int]
]]]
(* 1st *)
(* 2nd *)
(* x *)
Whoops? Why did this happen? Well, this is exactly the reason why If
holds its arguments! You do not want to evaluate both branches before you now which one is the correct one! However, by using Evaluate
, we evaluated both of them, so both Print
s occurred. What can you do now? Well, many different options:
- Ugly nested
With
s
With[{a = 1}, Evaluate[With[{int = int}, If[cond, Print["1st"]; int, Print["2nd"]; int]]]]
(* 1st *)
(* x *)
- or more elegantly, define
int
and cond
as functions
Clear[cond, int];
cond[a_] := a > 0;
int[a_] := ax;
With[{a = 1}, If[cond[a], Print["1st"]; int[a], Print["2nd"]; int[a]]]
(* 1st *)
(* x *)
Print
and that it should affect the evaluation process. $\endgroup$;
is also a functionCompoundExpression
. WhenEvaluate
is inside theCompoundExpression
, it no longer breaks throughHoldRest
attribute ofIf
. $\endgroup$"First possibility:"
and"Second possibility:"
. Which is nonsense. $\endgroup$Print
plays a substantial role here not justWith
andIf
. $\endgroup$