9
$\begingroup$

A wolfram Tutorial mentions that ReplaceAll and With are similar:

The way With[{x=x0, …}, body] works is to take body, and replace every occurrence of x, etc. in it by x0, etc. You can think of With as a generalization of the /. operator, suitable for application to Wolfram Language code instead of other expressions.

Where would they actually behave differently?

$\endgroup$
3
  • $\begingroup$ "ReplaceAll and With are similar."…which part of the tutorial are you refering to? I cannot spot it. $\endgroup$
    – xzczd
    Feb 19 at 9:20
  • 3
    $\begingroup$ "With" will rename conflicting local variables, "ReplaceAll" will not. $\endgroup$ Feb 19 at 10:08
  • $\begingroup$ "The way With[{x=x0,…},body] works is to take body, and replace every occurrence of x, etc. in it by x0, etc. You can think of With as a generalization of the /. operator, suitable for application to Wolfram Language code instead of other expressions." @xzczd $\endgroup$
    – ions me
    Feb 19 at 10:13

2 Answers 2

11
$\begingroup$

With is a scoping construct, meaning that symbols localized by With will be unaffected by whatever value they may have outside of it. Consider:

{x} /. x -> 2

{2}

Ok, looks like it did what we want it to. But what if x has a value?

x = 3;
{x} /. x -> 2

{2}

Surprisingly, the result is still the same. But beware: the code above does something insidious that's not immediately easy to spot. It's a typical case of the wrong method giving the desired result by accident. Try:

x = 3;
{x, 3} /. x -> 2

{2, 2}

See? The replacement didn't replace x; it replaced 3!

With, on the other hand does exactly what you think it does:

x = 3;
With[{x = 2}, {x, 3}]

{2, 3}

In general, ReplaceAll is a double edged-sword: it can be very powerful, but sometimes it's very difficult to understand exactly what it does and it can fail in all sorts of weird and interesting ways. Generally speaking, you should only use it when you really need to or when it's very easy to inspect the result and check that whatever came out is correct.

$\endgroup$
10
$\begingroup$

The way With[{x=x0, …}, body] works is to take body, and replace every occurrence of x, etc. in it by x0, etc. You can think of With as a generalization of the /. operator, suitable for application to Wolfram Language code instead of other expressions.

Except for the question of when x and body are evaluated, With[{x=x0, …}, body] works essentially like body /. x->x0.

Well, it's surprising to see these statements in the built-in tutorial. Personally I think they're confusing (what does "suitable for application to Wolfram Language code instead of other expressions" mean??) and misleading.

Indeed, in the sense that they can both be used for replacing, With and ReplaceAll (/.) are somewhat similar, but besides this, they're so different that I myself won't even try to compare them. Anyway, let me try to list. For the moment, I can recall 5 main differences:

  1. With has the attribute HoldAll, but ReplaceAll doesn't.

This has actually been mentioned in the 2nd cited paragraph in an elusive manner i.e. "Except for the question of when x and body are evaluated", and has been illustrated in Sjoerd Smit's answer. I'll add one more example that ReplaceAll (/.) is more suitable due to the absence of attribute like HoldAll, HoldFirst, etc.:

Clear[a];
expr = a + 1;
expr /. a -> 4
(* 5 *)

With[{a = 4}, expr]
(* 1 + a *)

Of course, for those with a good enough understanding for evaluation order of Mathematica, attribute like HoldAll is not a problem:

Clear[a];
With[{a = 4}, expr // Evaluate]
(* 5 *)

a = 123;
Unevaluated[a + 1] /. HoldPattern[a] -> 2
(* 3 *)

But these are advanced techniques, and it's more natural to turn to the function that fits your job.

  1. With sometimes renames the variable.

This is mentioned in Properties & Relations section of document of With and is also mentioned in Daniel Huber's comment above. The documented example is:

With[{e = x}, Function[x, e]]
(* Function[x$, x] *)

Function[x, e] /. e :> x
(* Function[x, x] *)

BTW, if you insist on using With in the above case, here's one possible way:

x = 123;
With[{e := x}, Function @@ Unevaluated@{x, e}]    
(* Function[x, x] *)
  1. More general replacement is allowed by ReplaceAll (/.)

With can only replace expression with head Symbol, while /. is essentially a function for pattern matching, the replacements allowed by /. are far more general. A few examples:

"a" /. "a" -> 3
(* 3 *)

Subscript[b, 1] /. Subscript[b, 1] -> 2
(* 2 *)

1 + Sin[c] + Cos[d] /. expr_Sin -> "aha"
(* 1 + "aha" + Cos[d] *)

g /. {{g -> first}, {g -> last}}
(* {first, last} *)

None of these can be done with With in a straightforward manner.

  1. The performances are different.

Generally, With is faster:

Clear[a];
With[{a = 1}, a] // RepeatedTiming
(* {2.25736*10^-7, 1} *)

a /. a -> 1 // RepeatedTiming
(* {8.04872*10^-7, 1} *)
  1. With is compilable.

When used inside Compile properly, With can be compiled, but /. isn't:

Needs["CompiledFunctionTools`"]

Compile[x, With[{a = 123}, a + x]] // CompilePrint

enter image description here

Compile[x, a + x /. a -> 123] // CompilePrint

enter image description here

$\endgroup$

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.