18
$\begingroup$

In this nice answer, Mr.Wizard writes

mk : MakeBoxes[(Hold | HoldForm | HoldComplete | HoldPattern)[__], _] :=
  Block[{$hldGfx = True, Graphics, Graphics3D}, mk] /; ! TrueQ[$hldGfx]

which is an application of the Villegas-Gayley pattern

Question: How does the Villegas-Gayley pattern work?

$\endgroup$
11
  • 5
    $\begingroup$ You can also have the condition on the LHS in the VG idiom, as I did in this answer. However, I'm confused as to what the question here is... $\endgroup$
    – rm -rf
    Jan 2, 2014 at 22:02
  • 3
    $\begingroup$ Jacob, I hope you won't take this badly, but @rm-rf isn't the only one left confused by this question. In fact, I think that your questions usually raise interesting points, but are sometimes posed in such an undirected way that answerers could be forgiven for not knowing where to begin. I know I'm not the best person to be saying this as I haven't asked any questions, but my strong impression is that yours get less attention than they deserve mainly due to their convoluted logical structure. Anyway, these mechanisms are IMO basically the same as those needed for tail call optimization. $\endgroup$ Jan 3, 2014 at 2:39
  • 2
    $\begingroup$ I voted to leave this open because it is an interesting topic. I agree with Oleksandr; your questions would get better attention of written more succinctly. I ask that you attempt to both shorten and focus this question. $\endgroup$
    – Mr.Wizard
    Jan 3, 2014 at 9:00
  • 1
    $\begingroup$ @rm-rf (and others), yes I can absolutely not blame you for being confused. I think I have more than one purpose in mind when asking a question. One purpose is always a sanity check: Does anything I say make sense, is there something I am missing? It is always nice to discuss such things. Another one here is that I think it would be nice if there is an "official" place to look for how Villegas-Gayley works. I could have focussed on answering how it works myself in an answer I suppose. But really I am only just learning about it. I am still unsure if there is a fundamental difference between... $\endgroup$ Jan 3, 2014 at 9:04
  • 2
    $\begingroup$ @JacobAkkerboom You can't overload Times... one of the few functions that you can't modify like you wish. $\endgroup$
    – rm -rf
    Jan 3, 2014 at 23:43

2 Answers 2

13
$\begingroup$

Say there's a function with only DownValues (as an example). The objective is to inject some wrapper code to it. You want to replace the function definition with your own code, but allowing your own code to call the unmodified function. For example, you might want to add preprocessing or postprocessing. All this, without requiring to modify existing definitions, either because of style or because of a real limitation as in the case of system functions. This is where the trick comes in.

How to do it

You need to prepend a definition that only matches when a variable is in a certain state. Then, while inside your function, you dynamically localize that variable so that it doesn't match the definition. Many built-ins whose definitions you can't access, will always try your custom definitions first so you might not need to worry about "prepending" the values. Example

Unprotect[Expand];
warn = True;
p_Expand /; warn /; ChoiceDialog["Sure?"] := Block[{warn = False},
   Print@"I'm about to expand";
   With[{exp = p},
    Print@"I expanded, here you go";
    exp
   ]
 ]

In this last case, the variable warn acts as a guard that you can modify to turn this definition on and off, in case you are interested. Otherwise, for safety, it may make sense to localize it and make it unique, for example, with a Module, as in

Module[{guard=True},
    fun[_]/;guard:=Block[{guard=False}, code] 
]

It seems the original version of the trick was intended to inject code in built-in functions. These functions live in the System` context. Your definition will also live in that context, since it is attached to the unprotected system symbol. As @Mr.Wizard warned in the comments, there's an evil lurking: the chance of Clear["Global`*"]. If your guard symbol lives in this context, then you will have a problem after clearing it. A solution that gets the best of both worlds is then

Module[{inside},
    fun[_]/;!TrueQ[inside]:=Block[{inside=True}, code] 
]

For those cases where the function returns unevaluated, you fortunately have the "cache bug" to prevent an infinite iteration. I don't think this "bug" is going anywhere, since this trick is used internally by Wolfram, and Update is a documented function.

$\endgroup$
22
  • $\begingroup$ One thing that is not clear from you example: ! TrueQ[var] (in the original) is used so that you don't have to explicitly set a global value for var; the test will by default fail. You then set var = True inside the Block. This both simplifies the code and makes it more robust, as there is no value that if cleared will break the code. Other, somewhat shorter, tests are possible but I stick with the original for convention and ease of recognition. $\endgroup$
    – Mr.Wizard
    Jan 6, 2014 at 22:22
  • 1
    $\begingroup$ You didn't include the Module here so your intention was not clear. Nevertheless if you want it to be robust against e.g. Clear["Global`*"] you will need to put the Symbol in a different context; this complicates things. Since the question as currently written asks for an explanation of V-G I think it would be better to describe the original rather than silently amending it. I can post my own answer if you prefer but I would rather see you include these things in yours. Perhaps you could start by explaining the original, then show your variation? $\endgroup$
    – Mr.Wizard
    Jan 6, 2014 at 22:34
  • 1
    $\begingroup$ @JacobAkkerboom It won't replace one of the DownValues since in the condition you are putting a unique symbol. $\endgroup$
    – Rojo
    Jan 7, 2014 at 12:09
  • 1
    $\begingroup$ @JacobAkkerboom Among DownValues, precedence is determined by pattern specificity only when that can be determined. Otherwise, they are tried in order. If you "prepend" some DownValue with a condition such as these ones, it will only try it after some definitions with no patterns. So, yes, it will fail in a function with standard memoization for example. But otherwise you are safe. $\endgroup$
    – Rojo
    Jan 7, 2014 at 12:11
  • 1
    $\begingroup$ @JacobAkkerboom Times is another layer of exception to the standard behaviour. It is particularly optimized to skip the evaluation procedure. She is not alone in that world, but there aren't too many, you can still treat them as exceptions and not get your general idea confused to make them fit $\endgroup$
    – Rojo
    Jan 7, 2014 at 12:13
2
$\begingroup$

Your post is actually several questions in one. I believe that in the future you should split such posts into several rather than grouping everything related into one.

One question appears to be about the placement of Conditions. Please see Placement of Condition /; expressions and post an answer if you have a fact-supported opinion about this. Regarding my code that you quote in the question I went with the RHS placement for the sake of line length for formatting.

Your proposed alternative doesn't work because the state of uq is inconsistent. Every call to the primary function will flip the state, therefore it fails entirely when the function is used repeatedly in the inner definition. Observe:

With[{uq = Unique[]},
 uq = False;
 func[x_] /; (uq = ! uq) := Print[{func[x], func[x]}]
]

$RecursionLimit = 20;

func[1]
{func[1],func[1]}

{func[1],Null}

{func[1],Null}

{func[1],Null}

{func[1],Null}

...

Also if you run your code multiple times you will get multiple definitions, all of which will interact and cause unintended behavior, though this could be corrected by using a named global instead of a Unique.

Since "Villegas-Gayley" has been design pattern for twenty years one could argue that it should be more recognizable and therefore more clear than alternatives. If you rewrite your question to focus on exactly what you think is unclear or counterintuitive about the Villegas-Gayley pattern perhaps we can address that. And unless or until you can provide a viable alternative there is little point is discussing hypothetical pros and cons.

$\endgroup$
7
  • $\begingroup$ I would argue that Villegas-Gayley proper also does not work nicely with functions that call themselves. The Block makes sure the "state variable" is always True, so calls further down do the user specified behavior anymore. The only function I was able to think of that calls itself more than once is MakeBoxes. I will test the behaviour of that without a front end. $\endgroup$ Jan 3, 2014 at 14:31
  • $\begingroup$ Interesting: Trace[ToBoxes[Expand[(1 + x)^4]], TraceInternal -> True] // Column $\endgroup$ Jan 3, 2014 at 15:11
  • $\begingroup$ I think my version works for MakeBoxes $\endgroup$ Jan 3, 2014 at 15:17
  • 3
    $\begingroup$ @JacobAkkerboom the objective of the pattern is to inject some code that will only be evaluated in the outermost call of a funcion, just once. If that's not what you intend then it's not the appropriate pattern $\endgroup$
    – Rojo
    Jan 3, 2014 at 16:30
  • 1
    $\begingroup$ @Rojo ah I was hoping for a reply by you :). Yes I suppose I found that out the hard way. Anyway I guess it is nice to know both ways. $\endgroup$ Jan 3, 2014 at 17:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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