Skip to main content
Added intermediate variable for threading over symbols, per OP's request
Source Link
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_] :=
   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]]]

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]]]

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, temp] /. Hold[d___] :>
        let[{temp = rhs, 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]]]
Fixed the evaluation leak
Source Link
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]Evaluate[rhs /. HoldPattern[With[{}, b_]] :> b]
   ];

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

let[{}, body_, {}, _] := 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]]]

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]
   ];

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

let[{}, body_, {}, _] := 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]]]

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]]]
Source Link
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]
   ];

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

let[{}, body_, {}, _] := 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]]]