Rules / Operators / concatenation

Concatenation Written

Joins two values as strings — and sits three precedence levels looser than arithmetic, which is where the surprises come from.

~
infixconcatenation 10/27list-assocRaku++ runs this — 1 of 7 results differdocs.raku.org ↗

~ glues its two operands together as strings. Like +, it does not require the right types; it imposes them.

Rules #

R1 It stringifies both operands and joins them

Each side is put in string context — .Str — so anything with a string form concatenates without a cast.

say "a" ~ "b";
say 1 ~ 2;
Output
ab
12

1 ~ 2 is "12", not 3. The operator decides the context, not the operands.

R2 A list stringifies to its elements joined with a space

This is .Str on a list, not a rule of ~. It is why building a message out of a list rarely gives what you meant.

say (1, 2) ~ "x";
Output
1 2x

R3 It is at concatenation, looser than arithmetic and replication

The chips above place ~ at concatenation 10/27. Multiplicative (7), additive (8) and replication (9) are all tighter, so arithmetic on either side happens first and you very rarely need parentheses around it.

say "n=" ~ 1 + 2;
say "x" x 2 ~ "y";
Output
n=3
xxy

Both read the way you want: "n=" ~ (1 + 2) and ("x" x 2) ~ "y". That ordering is deliberate — concatenation is the operator you almost always want to apply last.

R4 ~= appends in place

my $s = "a";
$s ~= "b";
say $s;
Output
ab

R5 It metaoperates like any other infix

[~] reduces a list with it, Z~ zips pairwise, X~ crosses.

say [~] <a b c>;
say ("a", "b") Z~ ("1", "2");
Output
abc
(a1 b2)

Traps #

T1 ~ is three different operators depending on position

The same character is an infix here, a prefix elsewhere, and part of several compound spellings. They are unrelated in meaning and in precedence.

SpellingCategoryMeaning
$a ~ $binfix, concatenation (10)concatenate
~$aprefix, symbolic unary (5)coerce to Str
$a ~~ $binfix, chaining (15)smartmatch
$a ~& $b, ~\, ~^infixstring bitwise ops

~$a is the idiomatic stringification, and being at symbolic-unary precedence it binds tighter than everything except method calls and **.

my @a = 1, 2, 3;
say ~@a;
say (~@a).WHAT;
Output
1 2 3
(Str)

T2 An undefined operand is a warning in Rakudo and an error in Raku++

Rakudo warns Use of Nil in string context (or uninitialized value) and treats the operand as the empty string. Raku++ diverges, and not gracefully:

engines differ
say Nil ~ "x";
Rakudo:  warns, then prints  x
Raku++:  No such method 'Nil' for invocant of type 'Str'

"a" ~ Any is the same story: Rakudo warns and prints a, Raku++ prints a silently. Either way, an undefined value reaching ~ means a missing // default somewhere upstream.

T3 Concatenating in a loop is quadratic

$s ~= … builds a new string each time. For anything longer than a few hundred appends, push onto an array and .join once.

my @parts;
@parts.push("x$_") for ^5;
say @parts.join(",");
Output
x0,x1,x2,x3,x4

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.

1 of 7 of these disagree between Raku++ and Rakudo — shown side by side below. A disagreement here is a defect report waiting to be written, not a documented rule.

ExpressionRaku++Rakudo
1 ~ 2Str | 12
"a" ~ "b"Str | ab
1 ~ "2"Str | 12
(1, 2) ~ (3, 4, 5)Str | 1 23 4 5
True ~ FalseStr | TrueFalse
Nil ~ 1ERR No such method 'Nil' for invocant of type 'Str'Str | 1
1/2 ~ 1/3Str | 0.50.333333