Rules / Operators / additive

Addition Written

Numeric addition — and, because it imposes numeric context, a coercion operator in disguise.

+
infixadditive 8/27left-assocRaku++ runs this — matches Rakudo on all 7docs.raku.org ↗

+ adds two numbers. Almost everything surprising about it comes from the second half of that sentence: it does not require numbers, it makes them, and what it makes depends on the type of each operand.

Rules #

R1 It adds its two operands numerically

say 2 + 3;
say -1 + 1;
Output
5
0

R2 Both operands are put in numeric context first

+ never concatenates and never checks that you meant a number. Each side is coerced by calling .Numeric on it, so anything with a numeric interpretation participates.

say "6" + "3";
say True + True;
Output
9
2

R3 A list operand numifies to its element count

This is not a special case in +; it is what .Numeric on a list means. It is the single most common way to be surprised by this operator.

say (1, 2) + (3, 4, 5);
say [10, 20, 30] + 1;
Output
5
4

(1, 2) becomes 2 and (3, 4, 5) becomes 3. If you wanted to add the contents, you wanted [+] — the reduction metaoperator — or Z+ to add pairwise.

say [+] 1, 2, 3, 4;
say (1, 2, 3) Z+ (10, 20, 30);
Output
10
(11 22 33)

R4 The result type is the widest of the operand types

Raku's numeric tower promotes rather than truncates. Int + Int stays Int, Rat + Rat stays Rat — exactly, with no binary-floating-point error — and a Complex anywhere makes the result Complex.

say (1/3 + 1/6).WHAT;
say 1/3 + 1/6;
say (1 + 2i).WHAT;
Output
(Rat)
0.5
(Complex)

0.1 + 0.2 is exactly 0.3 here, because both literals are Rat, not Num. That is a property of the literals, not of +: write 0.1e0 + 0.2e0 and you get the floating-point answer instead.

say 0.1 + 0.2 == 0.3;
say 0.1e0 + 0.2e0 == 0.3e0;
Output
True
False

R5 Int arithmetic does not overflow

Int is arbitrary-precision. Passing 64 bits is not an event.

say 2**64 + 1;
Output
18446744073709551617

R6 It is left-associative and additive-precedence

The chips above say additive 8/27 and left-assoc, which fixes two things at once: a + b + c groups as (a + b) + c, and + binds tighter than every level below the eighth — including x, xx, ~, and the comparisons.

say 1 + 2 ~ 3 + 4;
Output
37

That is (1 + 2) ~ (3 + 4), not 1 + (2 ~ 3) + 4. Concatenation sits at 10, two levels looser than additive.

Traps #

T1 x is looser than +, which reads backwards

Replication (level 9) is looser than additive (level 8), so the arithmetic on the right of x happens first.

say "-" x 3 + 2;
Output
-----

Five dashes, not "---" + 2. If you meant the other grouping, parenthesise it.

T2 Unary + is a different operator with much tighter precedence

+$x is prefix:<+> — a coercion to Numeric at symbolic unary precedence (level 5), far tighter than the infix at 8. It is the idiomatic way to force a string to a number, and it accepts every literal form Raku does.

say +"0x1f";
say +"1_000";
say +"3/4";
Output
31
1000
0.75

T3 A Nil operand contributes zero, with a warning on Rakudo

Rakudo warns Use of Nil in numeric context and continues with 0. Raku++ produces the same value but does not currently emit the warning.

say Nil + 1;
Output
1

Errors #

E1 A string that is not fully numeric throws

Numeric context is not a best-effort parse. A string with trailing junk is an error, not a silent 3:

say "3abc" + 1;
Rakudo:  Cannot convert string to number: trailing characters after number in '3<HERE>abc' (indicated by <HERE>)
Raku++:  Cannot convert string to number: trailing characters after number in '3abc'

Both engines refuse it. The only difference left is cosmetic: Rakudo marks the offending position with <HERE>, Raku++ does not.

This page previously recorded that Raku++ returned 4 here instead of throwing. That was true when the page was written and has since been fixed — which is worth knowing about the pages generally: a hand-written claim about a divergence is a snapshot, and only the examples are re-verified on every build.

See also #

Observed behaviour #

Each expression below was executed by both interpreters when this page was built; the results are recorded, not written. The operands are chosen to cross the boundaries that catch people out: string against number, a list where a scalar was meant, a boolean, an undefined value, and an exact rational.

Raku++ and Rakudo agree on every row below.

ExpressionRaku++Rakudo
1 + 2Int | 3
"a" + "b"rejected by both — Cannot convert string to number: base-10 number must begin with valid digits or '.' in 'a'
1 + "2"Int | 3
(1, 2) + (3, 4, 5)Int | 5
True + FalseInt | 1
Nil + 1Int | 1
1/2 + 1/3Rat | 0.833333