The difference is demonstrated by the following code snippet, where I've changed only the scoping construct. But let's first define a simple function:
f[x_]:=x+a Now we first look at Block:
Block[{a=b}, Print[a]; a=3; f[x]+a]+foo[a,b] (* Prints: b --> 6 + x + foo[a, b] *) As you see, the global value of a is temporarily assigned to b, then inside changed to 3. That is, not only the a inside the Block, but also the value of a in the function call from the Block is modified. Outside the block, the change is undone, including any change done inside the block (the a=3).
Let's now look at Module:
Module[{a=b}, Print[a]; a=3; f[x]+a]+foo[a,b] (* Prints: b --> 3 + a + x + foo[a, b] *) As you see, this time the a in the function does not evaluate to b. Indeed, the a inside the Module is replaced by a temporary variable, as can be seen by not assigning a value:
Module[{a},a] (* --> a$84 *) Finally, With:
With[{a=b}, Print[a]; a=3; f[x]+a]+foo[a,b] (* Prints: b --> 3 + a + x + foo[a, 3] *) As you can see, the a=3 now globally assigns 3 to b! That's because With actually replaces a with the "assigned" value, i.e. b in the whole body. That is, whereever the body contains a it's as if there was written b instead. But again, the value of a in the called function f is not affected.
From this, one can get the following guidelines:
In the general case, you want Module because its effect is most localized. You want Block is you explicitly want to temporarily change a global value, e.g. Block[{$RecursionLimit=10000},RecursiveFunction[10000]]. And With should be reserved for cases where you actually want a literal replacement.