With command does not behave as expected according to its Attributes

The attributes of the With command are HoldAll which means it should not evaluate its arguments before its starts its business. For example, if I try

f[arg_]:=(Print["evaluating"]; arg)


and then

With[{s=f[x]}, s + 1]


it should not evaluate f[x], right? But it does, i.e. it prints "evaluating", despite the fact that it should hold all its arguments.

I understand that body is probably held, so that the lexical substitutions can be made, and that is in line with the HoldAll instruction (attribute). But, the part that specifies constants (in {...}), that puzzles me. It is actually reasonable to evaluate it, but that contradicts fully the use of the HoldAll argument.

Would it not be better to use something like HoldRest as the attributes for With. Hmmm... I really do not understand this.

Does anyone understands this?

If a function is HoldAll, that means that the arguments are not evaluated before the function sees them, i.e. they're passed unevaluated into the function body. But once they're passed to the function, the function is free to do whatever it wants with them. This way HoldAll give you complete freedom in evaluating things in the order you want (or not evaluating them at all), at the expense of having to do more work to achieve the proper evaluation order.

Here is an example of how one might do this (see further examples at the end):

SetAttributes[fun, HoldAll]
fun[x_, y_] := Module[{z}, Hold[x, z] /. z -> y]

fun[1+2, 2+3]

(* ==> Hold[1+2, 5] *)


Setting the HoldAll attribute on fun allowed it to evaluate the second argument, keep the first argument unevaluated, then do something with both (i.e. put them inside a Hold[...]).

Getting back to With, let's look at why With couldn't be HoldRest. Notice that the first argument of With are not evaluated before With gets a change to do something with them. If they were, the complete expression {s=f[x]} would get evaluated and the global variable s would be set to x. With wouldn't be able to localize s. HoldAll (i.e. holding the first argument as well) makes it possible to prevent this and to only evaluate f[x] (which is how With works by design), but not {s=f[x]}.

Further examples

You could implement something very similar to With using Replace(All). If you try to do this, you'll see why HoldAll is absolutely necessary. I did something similar here.

Here's a very basic implementation of with, to illustrate that the HoldAll attribtue is absolutely necessary:

SetAttributes[with, HoldAll]
with[{s__Set}, body_] := body /. Apply[Rule[HoldPattern[#1], #2] &, Unevaluated[{s}], {1}]

with[{a=1,b=2}, a+b]

(* ==> 3 *)

(* a and b were not assigned values *)


(To be precise, With differs from this in some non-trivial ways relating to how it does localization through variable renaming sometimes, but this is irrelevant to this discussion.)

Another example illustrating that, as Leonid said, "the correct statement would be that [HoldAll functions] evaluate their arguments in a special way", or rather: the function may choose how, when and even how many times to evaluate them.

There are functions where the evaluation of an expression can be controlled with an option. See Plot:

Let's first define a function that will report whether it is evaluated with a numerical or symbolic argument.

ClearAll[f]
f[x_?NumericQ] := (Print["numeric evaluation"]; x^2)
f[x_] := (Print["symbolic evaluation"]; x^2)


If Plot were not HoldAll, then Plot[f[x], ...] would immediately evaluate to Plot[x^2, ...] and we'd get a single report "symbolic evaluation". Since Plot is HoldAll, it has the option to evaluate f[x] immediately and plot the resulting expression, or to first substitute in a number of x and evaluate it only afterwards. In fact it has an option, Evaluated, to control this.

Plot[f[x], {x, -1, 1}, MaxRecursion -> 0, PlotPoints -> 8, Evaluated -> False]

numeric evaluation
symbolic evaluation
numeric evaluation
numeric evaluation
numeric evaluation
numeric evaluation
numeric evaluation
numeric evaluation
numeric evaluation
numeric evaluation


With Evaluated -> False, we see that it first probes the function with a numeric argument, then it probes it with a symbolic one, finally it evaluates it in the 8 plot points. Or we could make it behave as if it didn't have HoldAll:

Plot[f[x], {x, -1, 1}, MaxRecursion -> 0, PlotPoints -> 10, Evaluated -> True]

symbolic evaluation


(Side note: that statement that it behaves as if it didn't have HoldAll is not 100% correct, in fact it will still make sure to localize x.)

• "before the function sees them", do you mean that when the function body is entered? it is not clear to me how it works. there are several stages, before the function is called, during the call of the function, and in the function body. At which stage HoldAll applies? – zorank Feb 27 '14 at 15:40
• " If they were, the complete expression {s=f[x]} would get evaluated and the global variable s would be set to x. " AHA! THANK YOU!!!! So the call to the f[x] is actually made within the body of the With command. Isn't it? – zorank Feb 27 '14 at 15:41
• Yes, that's right. The call to f[x] is made within the body of the With command. Here's an example of a HoldAll function that evaluates one argument but not the other. SetAttributes[f, HoldAll]; f[x_, y_] := Module[{z}, Hold[x, z] /. z -> y]. Try f[1+1, 2+2]. In fact I'd use With itself to implement it, but here I used ReplaceAll as your question was about With ... – Szabolcs Feb 27 '14 at 15:46
• @zorank In addition to the explanations in the above answer, have a look also at this post of mine, where there is a similar discussion of a meaning of a statement that Hold*-attributes carrying functions don't evaluate their arguments. Strictly speaking, such literal statements (even in the docs) are wrong, the correct statement would be that they evaluate their arguments in a special way. – Leonid Shifrin Feb 27 '14 at 16:43
• @zorank I got the time to update my answer. Let me know if there are any points that are not clear. – Szabolcs Feb 27 '14 at 18:04

Szabolcs already answered in detail the question that was asked, but I'd like to comment on an implicit question: how do you keep f[x] in your example from evaluating?

First realize that f[x] will be substituted into something which will evaluate, unless it has hold attributes such as these: Hold, HoldForm, Defer.

Then you need a way to keep f[x] from evaluating during the normal course of With as Szabolcs explained. Fortunately there is an undocumented syntax for With that does just this: use of := rather than = in its declaration list.

Together we have:

f[arg_] := (Print["evaluating"]; arg)

With[{s := f[x]}, Defer[s + 1]]

f[x] + 1


When the output is given as input:

evaluating

1 + x