Re: How to short-circuit match failure?
- To: mathgroup at smc.vnet.net
- Subject: [mg114465] Re: How to short-circuit match failure?
- From: kj <no.email at please.post>
- Date: Sun, 5 Dec 2010 21:50:48 -0500 (EST)
- References: <id7t63$lc6$1@smc.vnet.net>
In my previous post on this thread I wrote the following: > One workaround is to change Module to Block, since Block does not > create any temporary variables in the first place. Of course, > using Block is trickier than using Module, because there's the risk > that a block variable will localize the value of a *global* variable > that is used by some other function invoked during the evaluation > of the Block. (There may be a way to use Unique in a way that > automatically prevents this problem with Block without requiring > any particular care from the programmer, but, if so, it is beyond > my Mathematica skill to devise it.) Please, ignore the last parenthetical remark! It's insane at best (stupid at worst). I show a better solution below, one that still uses Module rather than Block, and thus retains Module's lexical encapsulation, but avoids the exploding context problem I referred to in my previous post. First, let me illustrate this "exploding context" problem. In preparation for this I define a symbol that will useful to examine the names in the current context: In[1]:= System`names := Names[$Context <> "*"] (I define it in the System` context to keep it out $Context (which is Global` by default), and still be able to refer to it with its unqualified name. Note that it's a SetDelayed definition, so evaluating this symbol will produce a state-dependent result.) Now I recapitulate the definition of foo I proposed before, (enhanced with the elegant simplification that Leonid Shifrin proposed in an earlier post): In[2]:= foo::toolong = "First argument is too long"; foo::nolist = "First argument is not a list"; foo::nargs = "foo called with `1` argument(s); 2 expected"; foo[args___] := Module[{chkargs, core}, chkargs[x_List /; Length[x] < 3, y_] := True; chkargs[_List, _] := Message[foo::toolong]; chkargs[_, _] := Message[foo::nolist]; chkargs[x___] := Message[foo::nargs, Length[{x}]]; core[x_, y_] := {#, y} & /@ x; core[args] /; chkargs[args] ] Now we observe the "exploding context" phenomenon. First, a single evaluation of foo: In[6]:= foo[{1}, 2]; If we examine the context now, we'll find a few lingering "zombies" of temporary variables, as I described in another thread (here I get to use the utility "names" symbol I defined in In[1]; the zombies are the ones whose names contain "$"): In[7]:= names Out[7]= {"args", "chkargs", "chkargs$", "core", "core$", "core$600", \ "foo", "x", "x$", "y", "y$"} In[8]:= Length[names] Out[8]= 11 Now, we evaluate foo many times, and afterwards count the current number of names in the context: In[9]:= Do[foo[{1}, 2], {1000}] In[10]:= Length[names] Out[10]= 1011 As you can see, one new name is added to the context per evaluation of foo. These names are all temporary variables whose names begin with core$: In[11]:= Select[names, StringMatchQ[#, "core$" ~~ __] &] // Short Out[11]//Short= {"core$1000", "core$1001", "core$1002", "<<995>>", \ "core$997", "core$998", "core$999"} In[12]:= Length[%] Out[12]= 1001 That's what I mean by an "exploding context." The solution below avoids this explosion, while still taking advantage of Module's lexical scoping. First, I clear the context (turning off messages that Remove emits when it is applied to a temporary variable; that's another zombie-related annoyance): In[13]:= Off[Remove::rmnsm]; Scan[Remove, names] On[Remove::rmnsm]; The new definition of foo is almost identical to the first definition, but it puts the Module scope *around* the SetDelayed instead of as its RHS. Hence, Module expression needs to be evaluated only once. Now, the SetDelayed's RHS is a single parentheses-delimited compound expression, needing no scoping of its own because it relies on the scoping provided by the surrounding Module: In[16]:= foo::toolong = "First argument is too long"; foo::nolist = "First argument is not a list"; foo::nargs = "foo called with `1` argument(s); 2 expected"; Module[{chkargs, core}, foo[args___] := ( chkargs[x_List /; Length[x] < 3, y_] := True; chkargs[_List, _] := Message[foo::toolong]; chkargs[_, _] := Message[foo::nolist]; chkargs[x___] := Message[foo::nargs, Length[{x}]]; core[x_, y_] := {#, y} & /@ x; core[args] /; chkargs[args]) ] Unfortunately, this new definition does not avoid zombies entirely: In[20]:= foo[{1}, 2]; In[21]:= names Out[21]= {"args", "args$", "chkargs", "chkargs$1601", "core", "core$1601", "foo", "x", "x$", "y", "y$"} But at least it avoids the "exploding context" problem: In[22]:= Length[%] Out[22]= 11 In[23]:= Do[foo[{1}, 2], {1000}] In[24]:= Length[names] Out[24]= 11 Importantly, this new definition produces the same output and emits the same messages as the original one: In[25]:= foo[{1, 2}, 3] Out[25]= {{1, 3}, {2, 3}} In[26]:= foo[{1, 2, 3}, 4] During evaluation of In[26]:= foo::toolong: First argument is too long Out[26]= foo[{1, 2, 3}, 4] Still, the new implementation's remaining zombies can still cause bizarre, difficult-to-debug core::shdw messages in some situations: In[27]:= Write["DEMO.m", OutputForm["BeginPackage[\"DEMO`\"]"]] Write["DEMO.m", OutputForm["Module[{core},1/;True];"]] Write["DEMO.m", OutputForm["EndPackage[]"]] Close["DEMO.m"]; In[31]:= Get["DEMO`"] During evaluation of In[31]:= core::shdw: Symbol core appears in multiple contexts {DEMO`,Global`}; definitions in context DEMO` may shadow or be shadowed by other definitions. >> The more I think about it, the harder it is for me to see these zombies as anything other than a bug in Mathematica. A "temporary variable" that lingers indefinitely is in breach of contract. Then again, I don't know how many versions of Mathematica have had this potential for such zombies. I imagine that if this behavior has survived long enough for me to find it, it probably is not causing too much problems in practice. Also, I am still very green with Mathematica contexts and packages (meaning that I am still routinely surprised and befuddled by context-related behaviors), so it is difficult for me from this inexperienced vantage point to have a clear idea of how much of a problem these zombies can be in practice. Thanks for reading this far. This was an instructive exercise for me. I hope some of you find it instructive too. ~kj