Put simply:
does
modifies an object in place (and should be used with caution with value types, see note below)
but
returns a new object.
When created off of a literal, it's probably not as evident, but when used with another object, it's pretty clear I think:
role R { method answer { 42 } }
my $question = 'question';
my $but = $question but R;
my $does = $question does R;
say $question.WHAT; # (Str+{R})
say $but.WHAT; # (Str+{R})
say $does.WHAT; # (Str+{R})
say $question.WHERE; # 129371492039210
say $but.WHERE; # 913912490323923
say $does.WHERE; # 129371492039210 <-- same as $question's
Notice I cheated a bit and swapped the order of does
and but
. If I had preserved the order you had, the does
would modify $question
in place, applying the role, meaning that but
would clone $question
(with its role) and apply the role (again!):
my $does = $question does R;
my $but = $question but R;
say $does.WHAT; # (Str+{R})
say $but.WHAT; # (Str+{R}+{R})
This is because does
as an operator is conceptually akin to ++
or +=
, that is, designed to be used in a standalone context, for instance
my $foo = …;
given $bar {
when 'a' { $foo does A }
when 'b' { $foo does B }
when 'c' { $foo does B }
}
Using but
is conceptually closer to using $foo + 1
— mostly meaningless unless assigned to or passed to something else.
A warning for does
and value types
If you use does
on a value type (strings, numbers mainly), there is an extremely high likelihood that you will cause unintended side effects. This is because value types (which, e.g., strings are) are supposed to be immutable and substitutable for one other. Note the following:
role Fooish { }
my $foo = 'foo';
$foo does Fooish;
say 'foo'.WHAT; # (Str+{Fooish})
This is a substitution that's happening at compile time (so it won't affect, e.g, 'foobar'.substr(0,3)
, that happens at runtime), but can cause some truly weird effects if you toss them in a loop:
role Fooish { }
my @a;
@a.push('foo' does Fooish) for ^10;
say @a[0].WHAT; # (Str+{Fooish}+{Fooish}+{Fooish}+{Fooish}+{Fooish}
+{Fooish}+{Fooish}+{Fooish}+{Fooish}+{Fooish})
Applying multiple rolls takes longer and longer the more you do it, so if you change that to ^100000, be ready to wait a while. OTOH, doing but
gives you nice constant time and doesn't pollute the literal. This behavior seems, AFAICT, to be perfectly valid, but definitely something that can catch you unexpectedly.