--- name: boolean operators in Perl --- # Operators Perl spells the boolean operators twice, in two different precedence classes. The C-style symbolic forms (`&&`, `||`, `!`) bind tightly; the word forms (`and`, `or`, `not`) bind very loosely. There is also `//` for the *defined-or* idiom, `xor` for exclusive-or, and the ternary `?:` which is logical selection in disguise. ## The full inventory | Operator | Reads as | Returns | Precedence | |----------|---------------------|---------------------------------------------------|------------| | `!` | not | normalised `1` or empty string | high | | `&&` | and | left if false, else right (the *operand*) | high | | `\|\|` | or | left if true, else right (the *operand*) | high | | `//` | defined-or | left if defined, else right (the *operand*) | high | | `xor` | exclusive or | normalised `1` or empty string | very low | | `not` | not | normalised `1` or empty string | very low | | `and` | and | left if false, else right (the *operand*) | very low | | `or` | or | left if true, else right (the *operand*) | very low | | `?:` | ternary | middle if condition true, else right (operand) | low | Two things in this table do most of the work: - **`&&` and `||` return an operand, not a boolean.** This is the property that makes Perl's idioms work the way they do. See below. - **The symbolic and word forms differ in precedence, not meaning.** `&&` and `and` compute the same logical function. They differ in how tightly they bind to other operators around them. That is why you see `or die` after function calls but `||` inside expressions — the chapter on the `or die` idiom explains. ## Short-circuit and the operand-return rule `&&` and `||` evaluate left-to-right and stop as soon as the result is determined. - `A && B` — if `A` is false, return `A` without evaluating `B`. Otherwise return `B`. - `A || B` — if `A` is true, return `A` without evaluating `B`. Otherwise return `B`. Crucially, the **return value is the operand itself**, not a normalised true/false. This is what makes patterns like `$config{port} || 8080` work: if `$config{port}` is set (truthy), the expression is `$config{port}`'s value; if not, it is `8080`. ```perl my $port = $config{port} || 8080; my $name = $user_input || "anonymous"; my $list = shift || []; ``` The same property powers `or die`: ```perl open my $fh, '<', $path or die "open $path: $!"; # ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ # if open returns true, the right-hand operand # the whole expression is never evaluated # is true and we move on ``` `open` returns true on success, undef on failure. On success the short-circuit stops on the left and `die` never runs. On failure the right-hand operand evaluates, which is the `die` call. ## `//` — defined-or `||` treats `0`, `""`, and `"0"` the same as `undef`. Sometimes that is wrong: a configured `0` is a real value, not a missing one. `//` short-circuits on **definedness** instead of truthiness: - `A // B` — if `A` is defined, return `A`. Otherwise return `B`. ```perl my $port = $config{port} || 8080; # 0 means "use default" — wrong my $port = $config{port} // 8080; # 0 means "0", undef means "use default" my $verbose = $opt{verbose} // 0; # 0 is a real choice ``` The compound assignment forms `||=` and `//=` save you from repeating the variable on the left: ```perl $config{port} //= 8080; # set only if currently undef $cache{$key} ||= compute($key); # set if currently false ``` ## `xor` and the absence of a high-precedence form There is no `^^` in Perl. `xor` exists only as the very-low-precedence word form, and unlike `and`/`or` it does *not* short-circuit (it cannot — both sides have to be evaluated to determine the result). It returns a normalised `1` or `""`, not an operand. ```perl if ($admin xor $guest) { ... } # exactly one of the two ``` For `xor` on values rather than on truthiness, the bitwise `^` applies to integers — covered in [the applications chapter](applications). ## `!` and `not` Both negate. `!` is the symbolic high-precedence form; `not` is the word low-precedence form. Both return a normalised value: `1` for "the operand was false", empty string `""` for "the operand was true". ```perl my $missing = ! $config{port}; # 1 if port is unset/0/""/"0" return if not @items; # cleaner reading than `if !@items` ``` ## The ternary `?:` `COND ? X : Y` is logical selection: if `COND` is true the value is `X`, otherwise `Y`. It is not just sugar for an `if`/`else` because it is an *expression* — it has a value, can be assigned, returned, passed as an argument. ```perl my $label = $count == 1 ? "1 item" : "$count items"; return $ok ? \%result : undef; ``` Beware nesting: `$a ? $b : $c ? $d : $e` parses as `$a ? $b : ($c ? $d : $e)` — chained `?:` is right-associative, which is usually what you want for an `if/elsif/else` cascade but is worth knowing rather than guessing. ## Why the word forms exist: precedence, not style `||` binds more tightly than `=`. `or` binds less tightly than `=` (or `,`, or anything else of conventional weight). That is the entire reason the word forms exist. ```perl my $fh = open $h, '<', $path || die "no $path: $!"; # ^^^^^^^^^^^^^^^^^^^^^^^^^^^ # parses as: open $h, '<', ($path || die "no $path: $!") # which is: open the file named "either $path or the die-message" # which is: catastrophe. my $fh = open $h, '<', $path or die "no $path: $!"; # parses as: ($fh = open $h, '<', $path) or die "no $path: $!" # which does what you meant. ``` Rule of thumb: - Inside an expression, where you want the value to flow into a variable or another operator: use `&&`, `||`, `!`, `//`. - After a statement, where you want a side-effect (`die`, `warn`, `return`) to fire conditionally: use `or`, `and`, `not`. ## What you should remember from this chapter - `&&` and `||` are not predicates that return 1 or 0 — they return one of their operands. This is the foundation of `||`, `||=`, `or die`, and most short defaulting idioms. - `//` is `||` for definedness, not truthiness. Use it when `0` is a legitimate value. - The word forms exist to bind looser than assignment, not for style. - `xor` exists, has no symbolic form, does not short-circuit.