A version of With that binds variables sequentially
- To: mathgroup at smc.vnet.net
- Subject: [mg94527] A version of With that binds variables sequentially
- From: "D. Grady" <D.C.Grady at gmail.com>
- Date: Mon, 15 Dec 2008 07:48:21 -0500 (EST)
Many times when I'm programming, I write code like this: With[{a1 = 5}, With[{a2 = f[a1]}, With[{a3 = g[a1, a2]}, h[a1, a2, a3]]]] I wish I could write this instead: With[{ a1 = 5, a2 = f[a1], a3 = g[a1, a2]}, h[a1, a2, a3]] Although With doesn't work this way, I didn't see any reason that it shouldn't, so I wrote a function called WithMany. It works by taking something in the second form above and holding all of the arguments, expanding out to the first form, and then releasing the hold. In spite of the simplicity of the idea, it was tricky for me to figure out the details of this function. I thought that someone else might find it helpful, and I also had some questions about it. Am I using a bad idiom with the nested With's? Is it better to just use Module and be done with it? Is there a way to get syntax highlighting for WithMany? I found the SyntaxInformation stuff, but it doesn't seem to expose the highlighting that's used for With, Module, or Block. Are there any cases where this function fails or does something unexpected? Is there a clearer way to write this function? It seems very complicated right now, especially in the fact that I need to use both a special symbol with attribute HoldAll and a special function with attribute HoldAll. Maybe someone sees a way to make it simpler? Really I'm just curious what other people think about this. Any comments would be appreciated. A simple example: WithMany[{ a1 = 5, a2 = RandomChoice@Range@a1, a3 = f[a2, a1]}, {a1, a2, a3}] {5, 1, f[1, 5]} The definition: ClearAll[WithMany]; SetAttributes[WithMany, HoldAll]; WithMany[args_, body_] := Module[{heldArgs, f, W, structure}, (* The very first thing that has to happen is to put args into a function with a hold attribute like HoldAll. Doing anything else means that args will be evaluated. *) heldArgs = Hold[args]; (* args is coming to us as a list of bindings. We want to replace the List with Hold. We can do this because Hold is wrapping everything right now. *) heldArgs = Apply[Hold, heldArgs, {1}]; (* Now we want to take the outermost Hold away so that we're left with Hold[bind1,bind2,bind3,...]. *) heldArgs = ReleaseHold[heldArgs]; (* Now we want to put each binding inside a List because With requires that syntax. *) heldArgs = Map[List, heldArgs]; (* Set up a symbol that will serve as a non-evaluating wrapper. *) SetAttributes[W, HoldAll]; (* Next define a function to use in the Fold operation. This function needs to have the attribute HoldAll, otherwise it will evaluate its arguments prematurely. *) f = Function[ {bod, binding}, W[binding, bod], {HoldAll}]; (* Use f to Fold W onto the bindings. This will create the correct structure without evaluating anything. *) (* If we had used With directly instead of W, then as soon as the first With statement is completely formed it will be evaluated, leaving the more outer With statements useless. *) (* Wrapping body in Unevaluated is neccessary to prevent body from being evaluated as soon as it appears in the arguments of Fold. *) structure = Fold[f, Unevaluated[body], Reverse@heldArgs]; (* The final step is to replace all of the W symbols with With. Replacement still works inside of Hold, and the symbol W is local to this module so there's no danger of messing up the input. *) structure /. W -> With ]