infix `but`

infix but (값에 롤 섞기)

기존 값에 롤(role)이나 클래스를 "섞어서" 그 기능만 추가한 변형을 만들고 싶을 때가 있어요. 원래 값은 그대로 두고 새 능력을 덧입힌 사본이 필요하다면, but 연산자가 딱 맞아요.

출처: Raku Documentation — infix but

본문

Operators에서

multi infix:<but>(Mu $obj1, Mu   $role) is assoc<non>
multi infix:<but>(Mu $obj1, Mu:D $obj2) is assoc<non>

$role을 섞은 $obj의 사본을 만들어요. $obj 자체는 수정되지 않으므로, but는 믹스인으로 불변 값을 만드는 데 쓸 수 있어요.

$role이 속성 하나만 제공한다면, 괄호 안에 초기화 값을 넘길 수 있어요.

role Answerable {
    has $.answer;
}
my $ultimate-question = 'Life, the Universe and Everything' but Answerable(42);
say $ultimate-question;         # OUTPUT: «Life, the Universe and Everything␤»
say $ultimate-question.^name;   # OUTPUT: «Str+{Answerable}␤»
say $ultimate-question.answer;  # OUTPUT: «42␤»

롤 대신 인스턴스화된 객체를 제공할 수도 있어요. 이 경우 연산자가 자동으로 롤을 만들어줘요. 그 롤은 $obj.^name과 같은 이름의 메서드 하나를 갖고, 그 메서드는 $obj를 돌려줘요.

my $forty-two = 42 but 'forty two';
say $forty-two+33;    # OUTPUT: «75␤»
say $forty-two.^name; # OUTPUT: «Int+{<anon|1>}␤»
say $forty-two.Str;   # OUTPUT: «forty two␤»

^name을 호출해 보면 그 변수가 익명 객체가 섞인 Int라는 걸 알 수 있어요. 그런데 그 객체는 Str 타입이기 때문에, 변수는 믹스인을 통해 그 이름의 메서드를 갖게 되고, 마지막 문장에서 그 메서드를 쓴 거예요.

클래스도 섞을 수 있어요. 심지어 그 자리에서 만들어 섞는 것도 가능해요.

my $s = 12 but class Warbles { method hi { 'hello' } }.new;
say $s.Warbles.hi;    # OUTPUT: «hello␤»
say $s + 42;          # OUTPUT: «54␤»

위처럼 섞인 클래스에 접근할 때는, 두 번째 문장에서 보이는 것처럼 클래스 이름을 그대로 사용해요. 같은 이름의 메서드가 이미 있다면 마지막에 섞인 롤이 우선해요. 괄호 안에 쉼표로 구분해 메서드 목록을 줄 수도 있는데, 이 경우 충돌은 런타임에 보고돼요.