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_] :=
Module[{temp},
partThread[s, rhs]temp] /. Hold[d___] :>
let[{temp = rhs, d, rest}, body, dec, syms];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]]]