control flow if

Documentation for control flow if, assembled from the following types:

language documentation Control Flow

From Control Flow

(Control Flow) control flow if

To conditionally run a block of code, use an if followed by a condition. The condition, an expression, will be evaluated immediately after the statement before the if finishes. The block attached to the condition will only be evaluated if the condition means True when coerced to Bool. Unlike some languages the condition does not have to be parenthesized, instead the { and } around the block are mandatory:

if 1 { "1 is true".say }  ; # says "1 is true" 
if 1   "1 is true".say    ; # syntax error, missing block 
if 0 { "0 is true".say }  ; # does not say anything, because 0 is false 
if 42.say and 0 { 43.say }# says "42" but does not say "43" 

There is also a form of if called a "statement modifier" form. In this case, the if and then the condition come after the code you want to run conditionally. Do note that the condition is still always evaluated first:

43.say if 42.say and 0;     # says "42" but does not say "43" 
43.say if 42.say and 1;     # says "42" and then says "43" 
say "It is easier to read code when 'if's are kept on left of screen"
    if True;                # says the above, because it is true 
{ 43.say } if True;         # says "43" as well 

The statement modifier form is probably best used sparingly.

The if statement itself will either slip us an empty list, if it does not run the block, or it will return the value which the block produces:

my $d = 0say (1, (if 0 { $d += 422}), 3$d); # says "(1 3 0)" 
my $c = 0say (1, (if 1 { $c += 422}), 3$c); # says "(1 2 3 42)" 
say (1, (if 1 { 22 }), 3);         # does not slip, says "(1 (2 2) 3)" 

For the statement modifier it is the same, except you have the value of the statement instead of a block:

say (1, (42 if True) , 2); # says "(1 42 2)" 
say (1, (42 if False), 2); # says "(1 2)" 
say (1,  42 if False , 2); # says "(1 42)" because "if False, 2" is true 

The if does not change the topic ($_) by default. In order to access the value which the conditional expression produced, you have to ask for it more strongly:

$_ = 1if 42 { $_.say }                ; # says "1" 
$_ = 1if 42 -> $_ { $_.say }          ; # says "42" 
$_ = 1if 42 -> $a { $_.say;  $a.say } ; # says "1" then says "42" 
$_ = 1if 42       { $_.say$^a.say } ; # says "1" then says "42" 

else/elsif

A compound conditional may be produced by following an if conditional with else to provide an alternative block to run when the conditional expression is false:

if 0 { say "no" } else { say "yes" }   ; # says "yes" 
if 0 { say "no" } else{ say "yes" }    ; # says "yes", space is not required 

The else cannot be separated from the conditional statement by a semicolon, but as a special case, it is OK to have a newline.

if 0 { say "no" }else { say "yes" }  ; # syntax error 
if 0 { say "no" }
else { say "yes" }                     ; # says "yes" 

Additional conditions may be sandwiched between the if and the else using elsif. An extra condition will only be evaluated if all the conditions before it were false, and only the block next to the first true condition will be run. You can end with an elsif instead of an else if you want.

if 0 { say "no" } elsif False { say "NO" } else { say "yes" } # says "yes" 
if 0 { say "no" } elsif True { say "YES" } else { say "yes" } # says "YES" 
 
if 0 { say "no" } elsif False { say "NO" } # does not say anything 
 
sub right { "Right!".sayTrue }
sub wrong { "Wrong!".sayFalse }
if wrong() { say "no" } elsif right() { say "yes" } else { say "maybe" }
# The above says "Wrong!" then says "Right!" then says "yes" 

You cannot use the statement modifier form with else or elsif:

42.say if 0 else { 43.say }            # syntax error 

All the same rules for semicolons and newlines apply, consistently

if 0 { say 0 }elsif 1 { say 1 }  else { say "how?" } ; # syntax error 
if 0 { say 0 }  elsif 1 { say 1 }else { say "how?" } ; # syntax error 
if 0 { say 0 }  elsif 1 { say 1 }  else { say "how?" } ; # says "1" 
if 0 { say 0 } elsif 1 { say 1 }
else { say "how?" }                                    ; # says "1" 
 
if 0 { say 0 }
elsif 1 { say 1 } else { say "how?" }                  ; # says "1" 
 
if        0 { say "no" }
elsif False { say "NO" }
else        { say "yes" }                              ; # says "yes" 

The whole thing either slips us an empty list (if no blocks were run) or returns the value produced by the block that did run:

my $d = 0say (1,
                (if 0 { $d += 42"two"} elsif False { $d += 432}),
                3$d); # says "(1 3 0)" 
my $c = 0say (1,
                (if 0 { $c += 42"two"} else { $c += 432}),
                3$c); # says "(1 2 3 43)" 

It's possible to obtain the value of the previous expression inside an else, which could be from if or the last elsif if any are present:

$_ = 1if 0     { } else -> $a { "$_ $a".say } ; # says "1 0" 
$_ = 1if False { } else -> $a { "$_ $a".say } ; # says "1 False" 
 
if False { } elsif 0 { } else -> $a { $a.say }  ; # says "0" 

unless

When you get sick of typing "if not (X)" you may use unless to invert the sense of a conditional statement. You cannot use else or elsif with unless because that ends up getting confusing. Other than those two differences unless works the same as if:

unless 1 { "1 is false".say }  ; # does not say anything, since 1 is true 
unless 1   "1 is false".say    ; # syntax error, missing block 
unless 0 { "0 is false".say }  ; # says "0 is false" 
unless 42.say and 1 { 43.say } ; # says "42" but does not say "43" 
43.say unless 42.say and 0;      # says "42" and then says "43" 
43.say unless 42.say and 1;      # says "42" but does not say "43" 
 
$_ = 1unless 0 { $_.say }           ; # says "1" 
$_ = 1unless 0 -> $_ { $_.say }     ; # says "0" 
$_ = 1unless False -> $a { $a.say } ; # says "False" 
 
my $c = 0say (1, (unless 0 { $c += 422}), 3$c); # says "(1 2 3 42)" 
my $d = 0say (1, (unless 1 { $d += 422}), 3$d); # says "(1 3 0)" 

with, orwith, without

The with statement is like if but tests for definedness rather than truth. In addition, it topicalizes on the condition, much like given:

with "abc".index("a"{ .say }      # prints 0 

Instead of elsif, orwith may be used to chain definedness tests:

# The below code says "Found a at 0" 
my $s = "abc";
with   $s.index("a"{ say "Found a at $_" }
orwith $s.index("b"{ say "Found b at $_" }
orwith $s.index("c"{ say "Found c at $_" }
else                 { say "Didn't find a, b or c" }

You may intermix if-based and with-based clauses.

# This says "Yes" 
if 0 { say "No" } orwith Nil { say "No" } orwith 0 { say "Yes" };

As with unless, you may use without to check for undefinedness, but you may not add an else clause:

my $answer = Any;
without $answer { warn "Got: $_" }

There are also with and without statement modifiers:

my $answer = (AnyTrue).roll;
say 42 with $answer;
warn "undefined answer" without $answer;