8

The docs state that "The simplest way to run a block where it cannot be a stand-alone statement is by writing do before it" and provide the following example:

# This dies half of the time 
do { say "Heads I win, tails I die."; Bool.pick } or die; say "I win.";

However, do doesn't seem to cause all blocks to run. In particular, it doesn't seem to run blocks with a signature:

do -> $a = 42 { say "ran with $a"; 0 } or die; say 'done'; # OUTPUT: «done»

So would it be better to say that do treats a block as an expression which sometimes causes it to be run? Or is Rakudo incorrect in its behavior here? Or is my understanding incorrect?

Elizabeth Mattijsen
  • 25,654
  • 3
  • 75
  • 105
codesections
  • 8,900
  • 16
  • 50
  • do -> $a { say "ran with $a"; 0 }(42) or die; say 'done'; – Holli Aug 11 '21 at 18:34
  • @Holli I understand how to invoke a block :-) But my point is that the `do` in that line doesn't seem to do anything – that produces the same output if you omit the `do`. – codesections Aug 11 '21 at 18:56

2 Answers2

5

Afaik:

  • Like all "statement prefixes", do consumes a "blorst" (block or statement) on its right and treats it as a statement.

  • do's only special power is that it evaluates to the value of the (last) statement. I think this is the only sense in which there's any sense of "expression" in its operation.

This interacts with the following behaviors, which are unrelated to do:

  • When a "bare" block ({...}) is treated as a statement, it runs the block.

  • Other blocks, treated as a statement, don't (or shouldn't), run.

I think the fix to the doc sentence is something like:

The simplest way to run a bare block where it cannot be is not behaving as a stand-alone statement is by writing do before it"

Though I don't know where/if the doc talks about the difference between bare blocks and other blocks.

raiph
  • 31,607
  • 3
  • 62
  • 111
  • Interesting. `say 0; do -> $a = 1 { say $a }; say 2` seems like it "ought" to print 0, 1, 2 – that is, saying to `do` a non-bare block feels like the same thing as calling the block with no arguments (just as `do` before a bare block is the same as calling that block with no arguments…) – codesections Aug 11 '21 at 19:21
  • 1
    Ponder what's in the issue I linked. Perhaps non-bare blocks recognized by the blorst rule will trigger a warning, or become a compile-time error, once RakuAST lands? – raiph Aug 11 '21 at 19:56
  • That is an inexact fix. It **is** behaving as a statement. It is also returning its final value, which is something that statements don't generally do. Also every block that is treated as a statement get run immediately. It is the blocks which are treated as expressions which do not run. (`$f = { $_ × 2 }`) – Brad Gilbert Aug 31 '21 at 00:02
  • @BradGilbert Thanks! .@codesections I'd like to delete my answer, because I'm confident Brad is right and my answer is badly misleading. Please unaccept my answer (and, I suggest, accept Brad's answer, which seems to me very clean and elegant, and which I know is grounded in a decade plus of growing mastery of both Raku and Rakudo's source, instead). TIA. – raiph Aug 31 '21 at 11:05
0

The do keyword turns a statement into an expression.

my @b = do for @a { $_² if $_ %% 2 }

my $c = do if @b > 4 { +@b } else { 3 }

A bare block can either be a statement:

my $a = 1;
say $a;
{
  my $a = 2;
  say $a;
}
say $a;

or it can be an expression:

my $code = { … }

When you use do with a bare block it acts as the statement form, but also returning the last value evaluated.

my $result = do { say 'hi'; 5 }
# hi

say $result;
# 5

A pointy block on the other hand is always an expression.

my $code = -> $ { … }

Even if you don't assign it anywhere

say 'a';
-> { say 'hi' }
do -> { say 'bye' }
say 'b';

# a
# b

It is pointless to use do with something that is already an expression.


Note that the do keyword in the compiler takes a blorst (block or statement) instead of just a statement because it doesn't make much sense to turn the block into a statement just to undo that and turn it into an expression-like block so that it will return its last value.

Basically blorst only ever captures a statement, but it captures the block type of statement directly to simplify the internals of the compiler.

Brad Gilbert
  • 33,846
  • 11
  • 78
  • 129