Scoping Revisited

With, Module, and Block

We finally have enough knowledge to understand the differences between the scoping constructs. We'll start by writing the same chunk of code with each:

 Module[{a=35},
a^2
]

 1225

With[{a=35},
a^2
]

 1225

Block[{a=35},
a^2
]

 1225


Everything is normal as of now. So let's try introducing some held expressions:

 Module[{a=35},
a^2//Hold
]


Hold[a$24242]    With[{a=35}, a^2//Hold ]   Hold[352]    Block[{a=35}, a^2//Hold ]   Hold[a2]   This alone should help in figuring out what is going on: • Module creates a new symbol with $ModuleNumber appended
• With inserts the given values into the expression

But what is Block doing? Nothing seems changed.

Let's look at a different example, just considering Module and Block as there is little more to discuss with With , although it should be noted that With is the cleanest and most useful of all these constructs.

Let's try define a global function:

 scopeProbe[x_]:=x*b;


First let's use it inside Module

 Module[{b=35},
scopeProbe[10]
]

 10 b


This behavior makes perfect sense. The b defined in Module is not the global b so this is what we'd predict.

 Block[{b=35},
scopeProbe[10]
]

 350


This here is the critical difference between Module and Block . Block reassigns the values of its symbols temporarily, while Module makes new symbols with temporary values (they have been given the attribute Temporary ).

This makes Block incredibly useful, but also potentially dangerous. Names used as variables to Block should always be made unique enough such that it's unlikely they'll have been used in a function that will be called by the Block .

An example of how we can use this is in memoization for a recursive function:

 recursiveFunction[arg_]:=Block[{recursiveFunctionmemoPad=<||>},
recursiveStep[arg]
];
recursiveStep[arg_]:=
If[!KeyMemberQ[recursiveFunctionmemoPad,arg],
recursiveFunctionmemoPad[arg]=If[Length@arg==0,
Pause[.5];
Total@ToCharacterCode@ToString@arg,
recursiveStep/@(List@@arg)//Total
],
recursiveFunctionmemoPad[arg]
];


And to see that this is doing what we expect

 recursiveFunction[a[a,a,a,a,a]]//AbsoluteTiming

 {0.500925,485}


While without memoization this should take about 2.5 seconds:

 recursiveNoMemo[arg_]:=If[Length@arg==0,
Pause[.5];
Total@ToCharacterCode@ToString@arg,
recursiveNoMemo/@(List@@arg)//Total
]

recursiveNoMemo[a[a,a,a,a,a]]//AbsoluteTiming

 {2.506773,485}


And just to check that we the Block wrapper does something:

 recursiveStep@a[a,a,a,a,a]

KeyMemberQ::invas: The argument GlobarecursiveFunctionmemoPad is not a valid Association or rule.
 If[!KeyMemberQ[GlobalrecursiveFunctionmemoPad,a[a,a,a,a,a]],GlobalrecursiveFunctionmemoPad[a[a,a,a,a,a]]=If[Length[a[a,a,a,a,a]]==0,Pause[0.5];Total[ToCharacterCode[ToString[a[a,a,a,a,a]]]],Total[recursiveStep/@List@@a[a,a,a,a,a]]],GlobalrecursiveFunctionmemoPad[a[a,a,a,a,a]]]
`