infix `but`
infix but (값에 롤 섞기)
기존 값에 롤(role)이나 클래스를 "섞어서" 그 기능만 추가한 변형을 만들고 싶을 때가 있어요. 원래 값은 그대로 두고 새 능력을 덧입힌 사본이 필요하다면, 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»
위처럼 섞인 클래스에 접근할 때는, 두 번째 문장에서 보이는 것처럼 클래스 이름을 그대로 사용해요. 같은 이름의 메서드가 이미 있다면 마지막에 섞인 롤이 우선해요. 괄호 안에 쉼표로 구분해 메서드 목록을 줄 수도 있는데, 이 경우 충돌은 런타임에 보고돼요.