In this example, Block is used to localize the variable cache as used in the function g when called from the function f. In f, the cache symbol to use is passed in as the second parameter z:

f[a_, z_] := Block[{cache = z}, g[a]];
g[a_] := (Print["hash" -> Hash[cache]]; cache[1] = a; cache[1]);

Everything works as expected:

2065959314 (your number may be different) 

Calling f:

f[1, z] 
hash->2065959314 (same) 

The symbol z now has a single downvalue:

{HoldPattern[z[1]] :> 1} 

However, what if I use the actual symbol cache as the cache symbol (say. by accident; not knowing that is the symbol name used inside Block):

1028301578 (your number may be different) 

Calling f:

f[1, cache] 
hash->1028301578 (same) 

But nothing stored in the cache this time?


My (partial) theory is that Block[{cache = cache}, ...] may have used a temporary variable cache$xxx after cache=cache collapses, but that doesn't explain why we still see the correct hash for the symbol inside g. This is an awkward, but OK situation if you know the name of the variable used inside Block, but could conceivably lead to some very puzzling bugs (as it did for me) if you chance upon the same symbol name.

  • 2
    $\begingroup$ As noted in the tag description, please do not add the bugs tag until your report has been confirmed by other people. $\endgroup$ Jun 6 '13 at 17:10

This is as designed (as it should be). In general, when you use

Block[{s = someExpression}, body]

then s is initialized with the value of someExpression, computed using the surrounding environment (usually Global` context). But then, all changes made to the properties of s, remain local to Block and are undone after the execution exits Block. In the particular case

Block[{cache = cache},...]

The assignments done in g are performed on the localized version of cache, and are therefore undone when Block exits, so everything works as it should.

A side note is that I would not recommend using code which changes the values of symbols which are not passed to it as parameters. The reasoning is basically the same as the usual recommendations against using global variables. So, the code like

g[a_] := (Print["hash" -> Hash[cache]]; cache[1] = a; cache[1]);

is inherently prone to errors, because it modifies the symbol cache that has not been passed to it. The puzzle you observed is just one manifestation of this.

  • 1
    $\begingroup$ Short and on point. :) $\endgroup$ Jun 6 '13 at 18:02
  • 6
    $\begingroup$ Thanks. I guess I should consider the former as a major achievement:) $\endgroup$ Jun 6 '13 at 18:04
  • $\begingroup$ Edited to make it 2 characters shorter, +1 $\endgroup$
    – Rojo
    Jun 7 '13 at 3:54
  • 1
    $\begingroup$ @user880 Thanks for the accept. Re:bug - this is most certainly not a bug. Rather, this is an inappropriate use of Block. I have argued many times in my answers on this site and elsewhere that Block is in 99 % of cases not a proper tool to localize variables in functions - With and Module are. Rather, Block is a special-purpose scoping construct, with typical use cases quite different from those for lexical ones. In particular, Block does not create a new symbol but rather temporarily changes the value or other properties of an existing symbol. $\endgroup$ Jun 7 '13 at 12:17
  • 1
    $\begingroup$ @user880 In your case, you should have explicitly passed the symbol you want to modify, into your g function, and not use Block as a short-cut (but rather use Module if you really wanted to guarantee the uniqueness of your cache symbol). The necessity of avoid naming collisions clearly calls for lexical scoping, yet you used dynamic scoping instead - an invitation for trouble. In general, if you like to work with symbols like that, it would be good to get a deeper understanding of scoping in Mathematica. $\endgroup$ Jun 7 '13 at 12:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.