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$
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.

| improve this answer | |
$\endgroup$
  • $\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$ – Sylvain Leroux Dec 21 '19 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.

| improve this answer | |
$\endgroup$
  • $\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$ – Sylvain Leroux Dec 21 '19 at 23:06
  • 2
    $\begingroup$ @Sylvain take a look at this: What are the use cases for different scoping constructs? $\endgroup$ – MarcoB Dec 22 '19 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}
| improve this answer | |
$\endgroup$
  • 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 '19 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$ – Sylvain Leroux Dec 21 '19 at 23:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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