19

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?

Jacob Akkerboom
  • 12,215
  • 45
  • 79
  • 5
    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... – rm -rf Jan 02 '14 at 22:02
  • 3
    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. – Oleksandr R. Jan 03 '14 at 02:39
  • I admit I'm confused, too. But as I read this post, it is far too broad and appears to ask for discussion of programming techniques rather than asking a specific question. I voting to close. – m_goldberg Jan 03 '14 at 03:06
  • 2
    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. – Mr.Wizard Jan 03 '14 at 09:00
  • 1
    @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... – Jacob Akkerboom Jan 03 '14 at 09:04
  • Condition on the left hand side vs Condition on the right hand side. I suppose I could write separate questions about this infinite recursion behaviour as well as about Condition. I am just afraid I will make an even bigger mess :P – Jacob Akkerboom Jan 03 '14 at 09:13
  • 1
    Jacob, I'm only half awake today which might be the problem, but I don't understand you. What is the code in your edit supposed to do? Also, the Times code you posted in the chat room causes an infinite loop on my system, but again I don't understand what it is supposed to do. Rather than posting a wall of code would you please step back and explain what you would like to accomplish? – Mr.Wizard Jan 03 '14 at 21:31
  • @Mr.Wizard I will get back to you when I am myself well rested. I don't understand why the code in chat would give an infinite loop. I ran it in version 7 and there was no infinite loop for me. The only issue there was that I seem to be unable to overload Times. I wanted to use code overloading Times in an example, but it doesn't matter. – Jacob Akkerboom Jan 03 '14 at 21:52
  • 2
    @JacobAkkerboom You can't overload Times... one of the few functions that you can't modify like you wish. – rm -rf Jan 03 '14 at 23:43
  • Would you mind including your alternate method in the question so that my answer may remain? I think it is an interesting illustration of the subtlety of this kind of thing. – Mr.Wizard Jan 06 '14 at 22:26
  • @Mr.Wizard I still want to make another example to show you that my alternate method works and there is no inconsistent state if used correctly. I will probably make a new Q&A, where you can assault my walls of code :P. I realise it may have been more convenient to have the discussion about this code in this Q&A, but oh well. – Jacob Akkerboom Jan 07 '14 at 11:21

2 Answers2

13

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.

Rojo
  • 42,601
  • 7
  • 96
  • 188
  • 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. – Mr.Wizard Jan 06 '14 at 22:22
  • @Mr.Wizard I prefer this one. I don't see the problem in explicitly setting something, it's just a few characters. I also don't see the problem in using a variable. Specially for something whose behaviour actually depends on a variable. Should you use that same variable name var in the TrueQ way, somewhere else, and it will break. So, a localized unique variable as in a Module makes more sense to me, instead of trying to get creative with the name. I don't think it is time significant but you also save one evaluation by avoiding TrueQ. – Rojo Jan 06 '14 at 22:28
  • 1
    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? – Mr.Wizard Jan 06 '14 at 22:34
  • By the way, one can still use Module or other methods to create a Symbol name to use in the original. – Mr.Wizard Jan 06 '14 at 22:35
  • @Mr.Wizard You are right about the context, I'll edit that in now. I honestly don't know what the original is; in my head the trick is in the concept. But this is just my answer. Feel free to post your own, or make a canonical community wiki. – Rojo Jan 06 '14 at 22:36
  • OK, I'm wrong. I was thinking of this form as the original, but looking again at the Notebook referenced in the comments a global variable is used. Anyway, you already have my +1. :-) – Mr.Wizard Jan 06 '14 at 22:43
  • Rojo, thanks for your answer. In the first sentence you mention DownValues. I think this is slightly confusing, as I think you don't really mean DownValues. Some System symbols have actual DownValues, e.g.System'Dump'AutoLoad as can be seen from (ClearAttributes[System'Dump'AutoLoad, ReadProtected]; System'Dump'AutoLoad // DownValues). I think a word for what you mean is downcode. Also note that Module places the local variable in the same Context as fun. Begin["Test'"]; Module[{x}, b = Hold[x]]; {Context[b], Context @@ b, End[]}[[;; 2]] – Jacob Akkerboom Jan 07 '14 at 11:12
  • Sorry for the screwy contexts – Jacob Akkerboom Jan 07 '14 at 11:15
  • @JacobAkkerboom Module will place the local variable in the current context, which won't be the System` context in general where your definition will be when you use this trick to inject code in system functions – Rojo Jan 07 '14 at 11:39
  • As to the DownValues, it was an example, to make it simple. If the symbol also had OwnValues this can get uselessly more confusing. I don't know if I meant downcode because I don't know what that is :P, hehe – Rojo Jan 07 '14 at 11:43
  • @Rojo oops! thanks for the reply, yes of course the function you try to overload will remain in System', derp :P. – Jacob Akkerboom Jan 07 '14 at 11:46
  • But I think the point about DownValues vs downcode (or something) is valid. I think if you try to overload System'Dump'Autoload, it may replace one of the DownValues (if you are unlucky). Also the code may not behave the same as the fact that overloaded definitions by the user take precedence over internal definitions may rely on the fact that in these case DownValues take precedence over downcode, whereas precedence among DownValues is determined by pattern specificity. Basically any symbol with attribute ReadProtected is unsuitable for this I'd say, as well as Times, oddly. – Jacob Akkerboom Jan 07 '14 at 11:57
  • @JacobAkkerboom hehe, I think you need to work on your ability to split an issue in parts. It seems to me totally correct to consider, as an example, the issue of a symbol with only regular downvalues, to explain the trick concept. After that part is clear, one can think about what happens with symbols with more complex definitions. – Rojo Jan 07 '14 at 12:08
  • 1
    @JacobAkkerboom It won't replace one of the DownValues since in the condition you are putting a unique symbol. – Rojo Jan 07 '14 at 12:09
  • 1
    @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. – Rojo Jan 07 '14 at 12:11
  • 1
    @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 – Rojo Jan 07 '14 at 12:13
  • @JacobAkkerboom Could you explain to us what you mean by downcode? – sebhofer Jan 07 '14 at 12:19
  • @Rojo all good points! Really I'd like to see all of them in answers, though admittedly probably not in this Q&A. – Jacob Akkerboom Jan 07 '14 at 12:37
  • @JacobAkkerboom perhaps we could clean up the useless comments here so those points are clear from the comments – Rojo Jan 07 '14 at 12:41
  • @JacobAkkerboom actually, values are always tried in order. It is just that when you add a value, it might get reordered. Unless you set SetSystemOptions["DefinitionsReordering" -> "None"] :P – Rojo Jan 07 '14 at 12:42
  • Guys, please take the chat to the Chat Room. – Mr.Wizard Jan 07 '14 at 13:38
2

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.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • 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. – Jacob Akkerboom Jan 03 '14 at 14:31
  • Interesting: Trace[ToBoxes[Expand[(1 + x)^4]], TraceInternal -> True] // Column – Jacob Akkerboom Jan 03 '14 at 15:11
  • I think my version works for MakeBoxes – Jacob Akkerboom Jan 03 '14 at 15:17
  • See updated question – Jacob Akkerboom Jan 03 '14 at 16:12
  • 3
    @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 – Rojo Jan 03 '14 at 16:30
  • 1
    @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. – Jacob Akkerboom Jan 03 '14 at 17:16
  • @JacobAkkerboom it's just that, as others said, your question raises too many issues for a single question. Even though I may have guesses on some, I don't feel it's worth writing a long collection of guesses that are probably not entirely true. – Rojo Jan 03 '14 at 20:29