Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`does` versus `but` operators when mixing in a Role into an object in Raku

If I have a Role R defined as:

role R { method answer { 42 } }

What is the difference (if any) between these two lines:

my $a = 'question' does R;
my $b = 'question' but  R;

They appear very similar:

say $a.answer;  # OUTPUT: «42»
say $b.answer;  # OUTPUT: «42»

say $a.WHAT;    # OUTPUT: «(Str+{R})»
say $b.WHAT;    # OUTPUT: «(Str+{R})»

Is this a case of there being More Than One Way To Do It™, and these both mean the same thing? Or is there a subtle difference that I'm missing?

note:
I understand that does is both an operator and a trait and thus can be used when for compile-time mixins (e.g., class C does R {}) whereas but is only for runtime mixins. I also understand that but can be used with an object (e.g., my $c = 'question' but False) whereas does can only be used with a Role. I'm not asking about either of those differences; my only question is about whether there's a difference when both are used at runtime with a Role. I have read the documentation section on mixing in Role, but didn't see an answer.

like image 352
codesections Avatar asked Feb 28 '21 16:02

codesections


People also ask

What is the ⩶ operator in Rakudo?

===uses the WHICHmethod to obtain the object identity. If you want to create a class that should act as a value type, then that class must create an instance method WHICH, that should return a ValueObjAtobject that won't change for the lifetime of the object. Since Rakudo version 2021.07, ⩶ is an alias for this operator.

What is the difference between $role and but operator?

Similar to butoperator, the $rolecan instead be an instantiated object, in which case, the operator will create a role for you automatically. The role will contain a single method named the same as $obj.^nameand that returns $obj: my $o = class { method Str { "original" } }.new;

What is precedence and associativity of Raku operators?

Operator precedence The precedence and associativity of Raku operators determine the order of evaluation of operands in expressions. Where two operators with a different precedence act on the same operand, the subexpression involving the higher-precedence operator is evaluated first.

Do non-infix operators have the same associativity in raku?

However, for operators built in to Raku, all operators with the same precedence level also have the same associativity. Setting the associativity of non-infix operators is not yet implemented. In the operator descriptions below, a default associativity of leftis assumed. Operator classification


Video Answer


1 Answers

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.

like image 151
user0721090601 Avatar answered Oct 18 '22 15:10

user0721090601