2
$\begingroup$

Functions like Table and Sum are scoped with Block, rather than Module.

For some particular applications, however, one might want to wrap these functions with Module to have lexical scoping of the iteration variables.

Is there a simple way to define new functions, let's call them Table2 and Sum2, that would do this automatically? That would save one from having to manually wrap Table and Sum inside Module all the time.

$\endgroup$
1
  • 1
    $\begingroup$ Can you show sample code where you need these new functions? $\endgroup$
    – MarcoB
    Dec 11, 2020 at 13:50

2 Answers 2

3
$\begingroup$

I think I found a reasonably simple way to do it.

I could not use Module directly, so I ended up reimplementing the lexical scoping by replacing the iteration variable with some variables generated by Unique.

Here is the code for an equivalent of Sum:

SetAttributes[Sum2, HoldAll]
Sum2[expr_, vars__] := Module[{repl},
  repl = (#[[1]] -> Unique[]) & /@ 
    ReleaseHold[MapAt[HoldPattern, Hold[{vars}], {1, All, 1}]];
  Sum[Unevaluated[expr] /. repl, Evaluate[Unevaluated[vars] /. repl]]
  ]
$\endgroup$
1
$\begingroup$

Assumption:

What OP wants to achieve is an alternate version of Sum where the symbols that are replaced by the summation index are only those that appear explicitly and not those that might appear inside the definition of the summand function.

In other words, they want a function that does this:

f[x_] := a x;

Sum2[f[a],{a,1,3}]
(*6a*)

as opposed to this

Sum[f[a],{a,1,3}]
(*14*)

Possible solution:

This can be achieved with Inactivate. It is similar to the more familiar Hold but it's transparent to the replacement done by Sum. Therefore this solution will do the job

SetAttributes[mySum2, HoldAll]
mySum2[expr_, vars__] := Activate[Sum[Inactivate[expr], vars]]

Differences with the other answer

Setting malicious UpValues

Inactivate doesn't stop standard evaluation, but rather it wraps all heads with the symbol Inactive so that there are no downvalues that apply. This means that clearly one could break my solution by doing

f /: Inactive[f] := 0&;
mySum2[f[a],{a,1,3}]
(*0*)

Order of evaluation

An important difference is the order of evaluation. The other answer does the same as Sum: expands the downvalues chain first, and then adds up the result. My answer instead adds everything up and then applies the rules.

In order to see this in practice we can put a Throw statement in the sum. The builtin Sum and also Sum2 will Catch it immediately while mine won't:

g[x_] := Throw[1] x;

Catch[Sum[g[a], {a, 1, 300000}]] // Timing
Catch[Sum2[g[a], {a, 1, 300000}]] // Timing
Catch[mySum[g[a], {a, 1, 300000}]] // Timing
(*
{0.000108,1}
{0.000115,1}
{0.546646,1}
*)

For a similar reason my sum is also significantly slower for infinite sums like Sum[x^n,{n,0,\[Infinity]}]

Performance for multiple sums

On the other hand, for multiple sums the logic in Sum2 is quite time consuming and my implementation is about a factor of 10 faster

With[{vars = Sequence @@ Table[{Unique[], 2}, 20]}, Sum[1, vars]] // Timing
With[{vars = Sequence @@ Table[{Unique[], 2}, 20]}, Sum2[1, vars]] // Timing
With[{vars = Sequence @@ Table[{Unique[], 2}, 20]}, mySum2[1, vars]] // Timing
(*
{0.035266, 1048576}
{14.9894, 1048576}
{1.03961, 1048576}
*)

OwnValues vs DownValues

Unfortunately Inactive does not screen against OwnValues, but only DownValues. This means that we have this counterintuitive behaviour

g[] := a;
Sum[g[], {a, 1, 3}]
Sum2[g[], {a, 1, 3}]
mySum2[g[], {a, 1, 3}]
(*
6
3a
3a
*)
h := a;
Sum[h, {a, 1, 3}]
Sum2[h, {a, 1, 3}]
mySum2[h, {a, 1, 3}]
(*
6
3a
6
*)

Note that assigning an OwnValue to a instead does not give any problems because the summation variables are always held.

$\endgroup$
1
  • $\begingroup$ Thank you for your answer, @MannyC. As you mentioned, it's unfortunate that it does not screen against OwnValues, but I really like its simplicity! $\endgroup$
    – dodosoft
    Dec 12, 2020 at 9:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.