3
$\begingroup$

This question already has an answer here:

I want the equivalent of Scheme's let*, or basically, a sequential With that works like this:

With[{a = 0,
      a = a + 1,
      a = a + 1},
      a]

Is there any way to implement this? Everything I tried with Hold/Unevaluated/etc. led nowhere.

$\endgroup$

marked as duplicate by István Zachar, Kuba, Leonid Shifrin, C. E., user9660 Mar 7 '16 at 13:13

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 1
    $\begingroup$ Interesting v10: wolfram.com/mathematica/new-in-10/inactive-objects/… $\endgroup$ – unlikely Mar 7 '16 at 10:11
  • 7
    $\begingroup$ Related: mathematica.stackexchange.com/questions/10432/… $\endgroup$ – unlikely Mar 7 '16 at 10:12
  • 11
    $\begingroup$ Add bracing for each level. In Mathematica 10.something we started to allow this. In[655]:= With[{a = 0}, {a = a + 1}, {a = a + 1}, a] Out[655]= 2. There is some amount of reddening in the user interface because it has not yet caught up to this change (fixing that is not entirely trivial). $\endgroup$ – Daniel Lichtblau Mar 7 '16 at 16:48
  • 1
    $\begingroup$ @DanielLichtblau: Thanks a lot for mentioning that, But ouch, that red is nasty! Any idea when it might get fixed? I actually use the syntax highlighting to help me figure out what variables are declared properly... $\endgroup$ – Mehrdad Mar 7 '16 at 19:19
  • $\begingroup$ @unlikely: Thanks a ton for the links, those are super helpful! $\endgroup$ – Mehrdad Mar 7 '16 at 19:20
6
$\begingroup$

This is outside the scope of With. The documentation says:

With[{x=x₀, y=y₀, ...}, expr]

specifies that all occurrences of the symbols x, y, ... in expr should be replaced by x₀, y₀, ...

So even if there was a "sequential" With, it wouldn't be able to understand a = a+1 as updating the value of a. It would always just be a replace rule.

I think you'd be best off with a Module:

Module[{a},
  a = 0;
  a = a+1;
  a = a+1;
  a]

You can write your own command which rewrites the form of the command for you, for example:

letstar[init_List, expr_] :=
  With[{vars = symbols[init]}, 
    Module[vars, CompoundExpression @@ Join[init, {expr}]]]
symbols[init_List] := Union@Hold[init][[1, All, 1]]
SetAttributes[symbols, HoldFirst]
SetAttributes[letstar, HoldFirst]

This assumes that the first argument of letstar is a list of assignments (this is not checked) and holds its form so that they are not performed. Instead, they are passed to symbols which only extracts the left-hand sides and lists unique variables appearing in them. This is passed to a Module as the local variables, the init block is converted into a compound expression and finally expr is evaluated. So if you call

letstar[{a = 0, a = a + 1, a = a + 1}, a]

this gets internally transformed to

Module[{a}, a = 0; a = a+1; a = a+1; a]

and returns

2

$\endgroup$

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