MathGroup Archive 2008

[Date Index] [Thread Index] [Author Index]

Search the Archive

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
  ]


  • Prev by Date: Re: Problem with HoldForm & Unevaluated
  • Next by Date: Evaluate ?
  • Previous by thread: Re: PDE with two variables by NDSolve
  • Next by thread: Re: A version of With that binds variables sequentially