실험적 기능
실험적 기능 (experimental)
Raku에는 아직 설계가 끝나지 않았지만 미리 사용해 볼 수 있게 열어 둔 기능들이 있어요. 이런 기능을 쓰겠다고 선언하는 방법과, 각 기능이 정확히 뭘 하는지를 설명하는 문서예요. 실제 제품 코드에 쓰기 전에 '이건 아직 실험 단계'라는 걸 꼭 기억해 두는 게 좋아요.
본문
Raku 개발 중에는 새로운 기능이 설계가 완성되기 전에 실험 기능으로 제공되는 경우가 많아요. 이 기능들은 나중에 Raku 사양의 일부가 될 수도 있고, 제거될 수도 있어요. 이 기능들을 쓰려면 프로그램 소스에서 experimental 프래그마를 사용하면 돼요.
use experimental :cached;
지금 시점에서 실험 단계인 기능들은 다음과 같아요.
pack
pack은 일반적인 데이터 구조를 이진 직렬화해서 Buf를 만드는 기능으로, Perl의 pack에서 물려받았어요. pack 순서는 unpack 설명에 나온 옵션들이 적힌 패킹 문자열에 따라 데이터 구조를 묶어서 Buf를 만들어요. 프로그램 시작 부분에 다음 프래그마를 넣으면 켜져요.
use experimental :pack;
예를 들어 숫자들을 16진수(H)로 해석해 묶되, 더 이상 요소가 없을 때까지 패턴을 반복하도록(*) 할 수 있어요.
use experimental :pack;
say pack("H*", "414243").contents;# OUTPUT: «(65 66 67)»
정확히 반대 일을 하는 대응 unpack 루틴도 있어요.
use experimental :pack;
my $buf=Buf.new(65,66,67);
say $buf.unpack("H*"); # OUTPUT: «414243»
위에 나온 기호들이 전부 구현된다는 보장은 없고, 이 단계를 벗어날 확정된 일정도 없어요. pack과 unpack에 대한 자세한 내용은 Blob 문서를 참고하세요.
macros
실험적 매크로는 언어 버전 6.e에는 제공되지 않을 예정이에요. 6.f에서 RakuAST를 사용하는 실제 매크로를 위한 길을 터 주기 위해서죠.
매크로는 프로그램이 실행되기 전인 컴파일 시간에 코드를 생성하는 루틴이에요. Raku에서 매크로 사용은 아직 실험 단계이며, 프래그마로 켜야 해요.
use experimental :macros;
매크로 처리는 파싱 시간에 일어나요. 매크로는 추상 구문 트리(abstract syntax tree)를 생성하고, 이것이 프로그램 구문 트리에 접목돼요. quasi가 바로 이 일을 수행하는 루틴이에요.
use experimental :macros;
macro does-nothing() {
quasi {}
};
does-nothing; # OUTPUT: «»
매크로는 루틴의 일종이라서 인자를 똑같은 방식으로 받을 수 있고, 동작도 거의 똑같아요.
use experimental :macros;
macro is-mighty( $who ) {
quasi { "$who is mighty!"}
};
say is-mighty "Freija"; # OUTPUT: « "Freija" is mighty!»
"거의"라는 말은 인자가 그 따옴표까지 포함한 리터럴로 삽입된다는 뜻이에요. 참고로 매크로 호출에서도 루틴과 같은 규칙으로 괄호를 생략할 수 있어요. 이런 걸 없애려면 언쿼팅(unquoting) 구성체 {{{}}}를 쓰면 돼요.
use experimental :macros;
macro is-mighty( $who ) {
quasi { {{{$who}}} ~ " is mighty!"}
};
say is-mighty "Freija"; # OUTPUT: «Freija is mighty!»
매크로 확장이 파싱 시간에 일어나기 때문에, 외부 변수를 매크로 안에서 사용할 때는 주의가 필요해요.
use experimental :macros;
my $called;
macro called() {
$called++;
quasi { "Called" }
};
say called() ~ " $called times";
say called() ~ " $called times"; # OUTPUT: «Called 2 timesCalled 2 times»
매크로는 파싱 시간에 확장되므로 $called는 런타임이 시작될 때 이미 2가 됐다 해서 그대로 출력돼요. 반대로 $called를 0으로 초기화하면, 그 초기화는 매크로가 확장되는 파싱 단계 이후에 실행되기 때문에 Called 0 times가 출력돼요.
매크로는 복잡한 계산 초기화를 처리해야 할 때 매우 유용해요. 그런데도 여전히 실험 보육원에 있는 건 이유가 있어요. 위에 보인 기능들이 크게 바뀔 가능성은 낮지만, 그 존재 자체를 포함해 무엇이든 필요에 따라 언제든 바뀔 수 있으니, 프로덕션 코드에서는 멀리 두는 게 좋아요. 한편 Masak의 이 글과 새 매크로 언어인 007을 살펴보면 앞으로 올 것들에 대한 실마리를 얻을 수 있어요.
cached
다음 프래그마는
use experimental :cached;
is cached 트레잇을 켜요. 이 트레잇은 루틴 호출 결과를 저장해 두고, 같은 인자로 다시 호출되면 같은 값을 돌려줘요.
무거운 계산이 들어갈 때 쓸 수 있어요. 2018 Advent Calendar에서 가져온 친화수(amicable numbers) 예제가 그렇죠.
use experimental :cached;
sub aliquot-parts( $number ) is cached {
(^$number).grep: $number %% *;
}
sub infix:<amic>( $m, $n ) {
$m == aliquot-parts($n).sum &&
$n == aliquot-parts($m).sum;
}
# Taken from https://en.wikipedia.org/wiki/Amicable_numbers
my @numbers = [2620, 2924, 5020, 5564, 6232, 6368, 66928, 66992];
say "Aliquot parts of $_ are ", aliquot-parts $_ for @numbers;
for @numbers X @numbers -> @pair {
say "@pair[0] and @pair[1] are ",
@pair[0] amic @pair[1]??" "!!"not ", "amicable";
}
이 코드는 aliquot parts 계산을 캐시해서, amic 연산자가 호출될 때 그 계산을 딱 한 번만 수행해요. 실제로 계산을 수행하는 건 이 aliquot parts를 출력하는 첫 번째 루프뿐이에요.
트레잇에 대한 추가 정보와 예시는 해당 문서를 참고하세요.
참고: 이 기능은 스레드에 안전하지 않아요.
rakuast
다음 프래그마는
use experimental :rakuast;
RakuAST 패키지와 그 클래스들을 6.e 이전의 언어 버전에서 사용할 수 있게 해줘요.