Skip to main content
2 of 3
Fixed the evaluation leak
Leonid Shifrin
  • 114.9k
  • 16
  • 333
  • 424

With this helper function:

SetAttributes[partThread, HoldAll];
partThread[l___, rhs_] :=
  Join @@ Replace[
    MapIndexed[Append[#, First@#2] &, Thread[Hold[{l}]]],
    Hold[s_, i_] :> Hold[s = rhs[[i]]],
    {1}];

The following modification of LetL seems to work according to your specs:

ClearAll[Let, let];
SetAttributes[{Let, let}, HoldAll];

Let /: Verbatim[SetDelayed][lhs_, rhs : HoldPattern[Let[__, _]]] := 
   Block[{With}, Attributes[With] = {HoldAll};
      lhs := Evaluate[rhs /. HoldPattern[With[{}, b_]] :> b]
   ];

Let[args___, body_] := let[{args}, body, {}, {}];

let[{}, body_, {}, _] := With[{}, body];

let[{Set[{s___}, rhs_], rest___}, body_, dec_, syms_] :=
   partThread[s, rhs] /. Hold[d___] :> let[{d, rest}, body, dec, syms];

let[
   {Set[sym_, rhs_], rest___}, 
   body_, 
   {decs___}, 
   {syms___}
] /; FreeQ[Unevaluated[rhs], Alternatives[syms]] :=
     let[{rest}, body, {decs, sym = rhs}, {syms, HoldPattern[sym]}];

let[{args___}, body_, {decs__}, _] :=
   Block[{With},
     Attributes[With] = {HoldAll};
     With[{decs},Evaluate[let[{args}, body, {}, {}]]]
   ];

This works quite similarly to LetL. What it does in addition to LetL is that it collects previous declarations into auxiliary lists stored as extra arguments of let, so that it can group together disjoint declarations. It also threads over arguments, using the partThread helper function. In all other respects it is the same code as LetL.

Here is your example:

f[x_Integer] := 
   Let[range = Range[2] + x, {a, b} = range, c = g[range], h[a, b, c]];

we can check what was generated:

?f

Global`f
  f[x_Integer]:=
    With[{range=Range[2]+x},
       With[{a=range[[1]],b=range[[2]],c=g[range]},h[a,b,c]]]
Leonid Shifrin
  • 114.9k
  • 16
  • 333
  • 424