트레잇(Traits)
트레잇(Traits) (traits)
Raku에서 _traits_는 객체와 클래스에 붙어 그 기본 동작·기능·표현을 수정하는 컴파일러 훅이에요. 컴파일러 훅이기 때문에 컴파일 타임에 정의되지만, 런타임에 쓸 수도 있어요.
언어 또는 Rakudo 컴파일러의 일부로 이미 여러 트레잇이 trait_mod 키워드로 정의되어 있어요. 이 문서에서 그것들을 차례로 나열하고 설명할게요.
is 트레잇
proto trait_mod:<is>(Mu $, |) {*}
is는 어떤 종류의 스칼라 객체에도 적용되며, 명명된 인자나 위치 인자를 몇 개든 받을 수 있어요. 가장 흔히 쓰는 트레잇이고, 첫 번째 인자의 타입에 따라 다음과 같은 형태를 취합니다.
클래스에 적용된 is
가장 흔한 형태로, 정의되는 클래스와 기존 클래스 두 개가 관련되며 부모 관계를 정의해요. 둘 다 클래스라면 A is B는 A를 B의 하위 클래스로 정의하죠.
is DEPRECATED는 클래스·속성·루틴에 적용할 수 있고, 그것들을 더 이상 사용되지 않는 것으로 표시하며 제공된 메시지가 있으면 출력해요.
is의 여러 용례는 그것이 가리키는 클래스의 속성으로 직접 번역돼요. rw, nativesize, ctype, unsigned, hidden, array_type이 그것이죠.
Uninstantiable 표현 트레잇은 표현 자체보다는 특정 클래스로 무엇을 할 수 있는지와 관련돼요. 이 트레잇은 어떤 방식으로든 클래스 인스턴스를 만드는 것을 사실상 막아요.
constant @IMM = <Innie Minnie Moe>;
class don't-instantiate is repr('Uninstantiable') {
my $.counter;
method imm () {
return @IMM[ $.counter++ mod @IMM.elems ];
}
}
say don't-instantiate.imm for ^10;
Uninstantiable 클래스는 위처럼 클래스 변수와 메서드를 통해 여전히 쓸 수 있어요. 하지만 my $do-instantiate = don't-instantiate.new;처럼 인스턴스화를 시도하면 You cannot create an instance of this type (don't-instantiate)라는 오류가 나요.
is repr과 네이티브 표현
is 트레잇은 일반적으로 적용된 클래스나 객체의 본질을 가리키므로, 네이티브 호출에서 네이티브 함수가 처리할 데이터 구조의 표현을 지정하는 데 is repr 접미사로 널리 쓰여요. 동시에 실제로 네이티브 함수로 구현된 루틴에는 is native가 쓰이죠. 다음 표현들을 쓸 수 있어요.
- CStruct — C 언어의
struct에 대응해요. 이질적이고 서로 다른 저수준 데이터 구조를 포함하는 복합 데이터 구조이며, 예시와 자세한 설명은 여기를 참고하세요. - CPPStruct — 비슷하게 C++의
struct에 대응해요. 다만 현재로서는 Rakudo 전용이에요. - CPointer — 이 언어들 중 하나의 포인터예요. 사용 전에 인스턴스화해야 하는 동적 데이터 구조로, 메서드도 네이티브인 클래스에 쓸 수 있어요.
- CUnion — C의
union과 같은 표현을 사용해요. 예시는 여기를 보세요.
반면 P6opaque는 Raku의 모든 객체에 쓰이는 기본 표현이에요.
class Thar {};
say Thar.REPR; # OUTPUT: «P6opaque»
메타객체 프로토콜이 달리 지정하지 않는 한 모든 객체와 클래스에 기본적으로 사용해요. 그래서 특별히 그 인터페이스로 작업하지 않는 한 일반적으로는 필요 없어요.
루틴에 적용된 is
메서드와 루틴 정의에는 우선순위와 결합성을 정하기 위해 is 트레잇을 쓸 수 있어요. 이것들은 추가될 트레잇의 타입과 이름을 인자로 받는 trait_mod로 정의된 서브처럼 동작해요. 서브루틴의 경우 트레잇은 클래스·역할 계층을 가로지르는 기능을 추가하거나, 독립적으로 정의된 루틴에 동작을 추가하는 방법이 돼요.
is implementation-detail
Rakudo 컴파일러 2020.05 릴리스부터 사용할 수 있어요. 이 트레잇은 Raku 언어 구현과 모듈 작성자가 특정 루틴(메서드 포함)이 공개 API의 일부가 아니라는 것을 표시하는 데 써요. 그런 루틴은 직접 조회하면 찾을 수 있지만, 인트로스펙션 결과에는 나타나지 않아요.
my &do-not-use-routine = CORE::<&DYNAMIC>;
say CORE::.keys.grep(* eq '&DYNAMIC'); # OUTPUT: «()»
이런 루틴은 사용자가 쓰라고 만든 것이 아니며, 동작과 가용성은 언제든 바뀔 수 있어요.
Rakudo 컴파일러 2021.02 릴리스부터는 is implementation-detail을 클래스와 역할에도 적용할 수 있어요.
method is-implementation-detail
method is-implementation-detail(--> True)
이 트레잇을 적용하면 Code에 호출되는 is-implementation-detail 메서드가 True를 돌려줘요. 그래서 앞으로 몇 년간 변경에도 이 코드를 유지할 의향이 없다면 쓰지 말라는 힌트를 사용자에게 주는 셈이에요.
my &fail-routine = &fail;
unless &fail-routine.is-implementation-detail {
say "&fail is not an implementation detail, can expect backward compatibility";
}
sub PRIVATE-CALCULATION is implementation-detail { #`(Not safe to rely on this) }
if &PRIVATE-CALCULATION.is-implementation-detail {
say "You better not to rely on &PRIVATE-CALCULATION unless you really know what you are doing";
}