Skip to content
AgileLogics
8 min read

Scoping and Evaluation Control

Module, Block, and With

The three scoping constructs look alike but work differently:

x = 10;
f[] := x
Module[{x = 20}, f[]]
Block[{x = 20}, f[]]
With[{c = 5}, c^2]
10
20
25
  • Module (lexical scoping) renames its variables to unique symbols like x$1234, so code outside the body — here f — never sees them. Use it for ordinary local variables.
  • Block (dynamic scoping) temporarily gives the global x a new value while the body runs, so everything called from the body sees it. Use it to change a setting temporarily, such as Block[{$RecursionLimit = 5000}, ...].
  • With replaces the names with constant values before the body is evaluated — fast, and the values can't be changed.

With is also how you insert a value into held code:

With[{n = 3}, Function[y, y^n]]
Function[y, y^3]

Evaluation order

Normally the Wolfram Language evaluates the head, then each argument, then applies definitions — repeating until nothing changes. Trace shows the steps:

Trace[2 + 3*4]
{{3 4, 12}, 2 + 12, 14}

Holding evaluation

Hold[1 + 2]
ReleaseHold[Hold[1 + 2]]
Hold[Evaluate[1 + 2]]
Attributes[Plus]
Hold[1 + 2]
3
Hold[3]
{Flat, Listable, NumericFunction, OneIdentity, Orderless, Protected}
  • Hold keeps its argument unevaluated; ReleaseHold removes it.
  • Evaluate forces evaluation inside a held position.
  • Attributes change how a function evaluates: Orderless (argument order doesn't matter), Flat (associative), Listable (maps over lists), HoldAll/HoldFirst/HoldRest (arguments aren't evaluated first), Protected (can't be redefined).

Giving your functions attributes

clip[v_] := If[v > 5, 5, v]
clip[{1, 7, 3}]
SetAttributes[clip, Listable]
clip[{1, 7, 3}]
If[{1, 7, 3} > 5, 5, {1, 7, 3}]
{1, 5, 3}

Without Listable, the whole list was passed in and the comparison {1, 7, 3} > 5 couldn't be decided, so If stayed unevaluated. With it, clip is applied to each element automatically.

A function with HoldAll receives its arguments as code — how you'd write your own control structure:

SetAttributes[unless, HoldRest];
unless[cond_, body_] := If[!cond, body]
unless[1 > 2, Print["ran"]]
unless[1 < 2, Print["not printed"]]
ran

Because of HoldRest, Print[...] isn't evaluated until If decides to.

Common mistakes

  • Using Block for local variables — any function called from the body that uses the same global name is affected. Default to Module.
  • Forgetting Evaluate in Plot[Evaluate[Table[x^n, {n, 3}]], {x, 0, 1}]: Plot holds its first argument, so without it the curves are computed but styled as a single function.