4
$\begingroup$

Is it possible to use a previously defined value in subsequent definitions of a Block?

According to syntax coloration, this should work as expected, that is, returning both the data list and its length:

Clear["data", "len"]
Block[{ 
    data = Range[5],
    len = Length[data]
  },{len, data}
]

However, the result is:

{0,{1,2,3,4,5}}

enter image description here

Surprisingly enough, this works:

Clear["data", "sum"]
echo = Block[{ 
   data = Range[5],
   sum = Plus[data, 1]
   }, {sum, data}
  ]

Producing:

{{2, 3, 4, 5, 6}, {1, 2, 3, 4, 5}}

Two questions:

  • Why is Length unable to see the definition of data--whereas Plus seems to have no trouble with that?
  • Most importantly, how can I ensure any given expression will make use of the proper data value?

FWIW, I suspect this should have to do with Block variables not being available immediately (as opposed to letrec construct commonly found in functional language). If I'm right this could be fixed using Hold*-related functions. But I was unable to find a solution by myself.

$\endgroup$

3 Answers 3

6
$\begingroup$

The values of the variables in a Block are set at definition time, when data has no value:

Clear["data","len"]
TracePrint[
    Block[
        {
        data=Range[5],
        len=Length[data]
        },
        {len,data}
    ],
    _Length
]

Length[data]

{0, {1, 2, 3, 4, 5}}

The difference between using Plus and Length is that when data has no value data + 1 just evaluates to data + 1 while Length[data] evaluates to 0. So, the sum variable still has data in it, and it's value can change when data acquires a value.

One possibility to achieve your goal is to use SetDelayed instead of Set in your Block:

Block[
    {
    data = Range[5],
    len := Length[data]
    },
    {len,data}
]

{5, {1, 2, 3, 4, 5}}

Another, more robust possibility is to use the currently undocumented varargs With syntax:

With[
    {data = Range[5]},
    {len = Length[data]},

    {len, data}
]

{5, {1, 2, 3, 4, 5}}

Support for this syntax is not going away, and I believe the syntax should soon become documented.

$\endgroup$
1
  • $\begingroup$ As always, @Carl, thank you for this enlightening answer. Speaking about the documentation, I didn't remember from reading the Block reference that we can actually use delayed affectation. Good to know!! $\endgroup$ Dec 21, 2019 at 22:54
4
$\begingroup$

It is possible to use Block with normal assignment, but you have to wait until data is available. (See the answer of @CarlWoll for the use of delayed assignment.)

Block[{data = Range@5, len},
 len = Length@data;
 {len, data}
 ]

However in this particular example, it is unclear why Module would not be preferable. Imo, Block should only be used when dynamic scoping is really needed, which is seldom.

$\endgroup$
2
  • $\begingroup$ Thanks, @Alan. The example given in the question is just a minimal example and certainly not my real use case. However, could you elaborate a little about the reason why Module would generally be preferable to Block? I'm new to Mathematica, and it is not always obvious to choose between With, Module and Block. $\endgroup$ Dec 21, 2019 at 23:06
  • 2
    $\begingroup$ @Sylvain take a look at this: What are the use cases for different scoping constructs? $\endgroup$
    – MarcoB
    Dec 22, 2019 at 5:09
1
$\begingroup$

I managed to find a solution using Hold and ReleaseHold. But I wouldn't swear this is an optimal one:

Clear["data", "len", "sum"]
echo = Block[{ 
   data = Range[5],
   len = Hold[Length[ data]],
   sum = Hold[Plus[data, 1]]
   }, ReleaseHold[{len, sum, data}]
  ]
{5, {2, 3, 4, 5, 6}, {1, 2, 3, 4, 5}}

I can't really answer the why question though since none of the Plus or Length functions have the Hold* attribute defined:

Attributes[Plus]
Attributes[Length]
{Flat, Listable, NumericFunction, OneIdentity, Orderless, Protected}
{Protected}
$\endgroup$
2
  • 2
    $\begingroup$ We could achieve the same effect using Block[{data = Range[5], len := Length[data]}, {len, data}]. But a more conventional solution would be to avoid the deferred execution using the somewhat repetitive but idiomatic Block[{data, len}, data = Range[5]; len = Length[data]; {len, data}]. Incidentally, Block is the analog to Scheme's fluid-let so the sequencing issue is not as likely to arise for Block as it would if instead we were using Module (= Scheme's let). $\endgroup$
    – WReach
    Dec 21, 2019 at 20:28
  • $\begingroup$ As I said in a comment to Carl's answer above, I completely missed the fact we can use delayed affectation :/ Anyhow, thanks for the comment @WR, and for mentioning the "idiomatic" way of solving the issue! $\endgroup$ Dec 21, 2019 at 23:03

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.