Code — 모든 코드 객체의 최상위 기반 클래스
Code — 모든 코드 객체의 최상위 기반 클래스
Raku에는 블록, 서브루틴, 메서드처럼 "실행되는 코드"를 나타내는 객체가 많아요. 그 모든 코드 객체가 공통으로 갖는 성질을 모아 둔 곳이 바로 Code 클래스입니다.
본문
class Code is Any does Callable {}
Code는 Raku에서 모든 코드 객체의 궁극적인 기반(최상위) 클래스예요. 모든 코드 객체가 갖는 기능을 노출합니다. thunk(지연 계산용 코드)는 정확히 Code 타입이지만, 블록·서브루틴·메서드에서 나온 대부분의 코드 객체는 Code의 어떤 서브클래스에 속할 거예요.
메서드
method ACCEPTS
multi method ACCEPTS(Code:D: Mu $topic)
보통 코드 객체를 호출하면서 $topic을 인자로 넘겨요. 다만 인자를 받지 않는 코드 객체에서 호출하면, 인자 없이 코드 객체를 호출하고 $topic은 버립니다. 호출 결과를 반환해요.
method arity
method arity(Code:D: --> Int:D)
코드 객체를 호출하려면 최소한 넘겨야 하는 위치 인자 개수를 반환해요. 코드 객체의 Signature에 있는 선택적(optional) 매개변수나 slurpy 매개변수는 세지 않고, 이름 매개변수도 세지 않아요.
sub argless() { }
sub args($a, $b?) { }
sub slurpy($a, $b, *@c) { }
say &argless.arity; # OUTPUT: «0»
say &args.arity; # OUTPUT: «1»
say &slurpy.arity; # OUTPUT: «2»
method assuming
method assuming(Callable:D $self: |primers)
assuming에 넘긴 인자로 프라이밍(priming)된 새 Callable을 반환해요. 달리 말하면, 새 함수는 원래와 같은 동작을 하지만 .assuming에 넘긴 값들이 이미 해당 매개변수에 바인딩되어 있어요.
my sub slow($n){ my $i = 0; $i++ while $i < $n; $i }
# 매개변수를 하나만 받으므로 $n을 전달하지 않아요
sub bench(&c) { c, now - ENTER now }
say &slow.assuming(10000000).&bench; # OUTPUT: «(10000000 7.5508834)»
arity(인수 개수)가 1보다 큰 서브루틴의 경우, "가정(assume)"하지 않을 위치 매개변수에는 Whatever *를 쓸 수 있어요.
sub first-and-last ( $first, $last ) {
say "Name is $first $last";
}
my &surname-smith = &first-and-last.assuming( *, 'Smith' );
surname-smith( 'Joe' ); # OUTPUT: «Name is Joe Smith»
가정한 매개변수와 가정하지 않은 매개변수의 조합은 아무렇게나 처리할 수 있어요.
sub longer-names ( $first, $middle, $last, $suffix ) {
say "Name is $first $middle $last $suffix";
}
my &surname-public = &longer-names.assuming( *, *, 'Public', * );
surname-public( 'Joe', 'Q.', 'Jr.'); # OUTPUT: «Name is Joe Q. Public Jr.»
이름 매개변수도 가정할 수 있어요.
sub foo { say "$^a $^b $:foo $:bar" }
&foo.assuming(13, foo => 42)(24, bar => 72); # OUTPUT: «13 24 42 72»
그리고 Methods와 Blocks를 포함한 모든 종류의 Callables에 .assuming을 쓸 수 있어요.
# invocant에는 Whatever 스타를 써요:
my &comber = Str.^lookup('comb').assuming( *, /R \w+/ );
say comber 'Raku is awesome! Ruby is great! And Rust is OK too';
# OUTPUT: «(Raku Ruby Rust)»
my &learner = {
"It took me $:months months to learn $^lang"
}.assuming: 'Raku';
say learner months => 2; # OUTPUT: «It took me 2 months to learn Raku»
method count
method count(Code:D: --> Real:D)
코드 객체를 호출할 때 넘길 수 있는 위치 인자의 최대 개수를 반환해요. 위치 인자를 얼마든지 받을 수 있는 코드 객체(즉 slurpy 매개변수를 가진 경우)라면 count는 Inf를 반환해요. 이름 매개변수는 세지 않아요.
sub argless() { }
sub args($a, $b?) { }
sub slurpy($a, $b, *@c) { }
say &argless.count; # OUTPUT: «0»
say &args.count; # OUTPUT: «2»
say &slurpy.count; # OUTPUT: «Inf»
method of
method of(Code:D: --> Mu)
Code의 반환 타입 제약을 반환해요.
say -> () --> Int {}.of; # OUTPUT: «(Int)»
method signature
multi method signature(Code:D: --> Signature:D)
이 코드 객체에 대한, 매개변수를 설명하는 Signature 객체를 반환해요.
sub a(Int $one, Str $two) {};
say &a.signature; # OUTPUT: «(Int $one, Str $two)»
method cando
method cando(Capture $c)
주어진 Capture로 호출할 수 있는 후보(candidate)들의 리스트를 반환해요. Code 객체는 다중 디스패치(multiple dispatch)가 없으므로, 객체를 담은 리스트거나 빈 리스트를 반환합니다.
my $single = \'a'; # 인자 하나짜리 Capture
my $plural = \('a', 42); # 인자 두 개짜리 Capture
my &block = { say $^a }; # 하나의 인자를 받는, Code의 서브클래스인 Block 객체
say &block.cando($single); # OUTPUT: «(-> $a { #`(Block|94212856419136) ... })»
say &block.cando($plural); # OUTPUT: «()»
method Str
multi method Str(Code:D: --> Str:D)
메서드 이름을 출력하되 경고도 만들어 내요. 대신 .raku나 .gist를 쓰세요.
sub marine() { }
say ~&marine;
# OUTPUT: «Sub object coerced to string (please use .gist or .raku to do that)marine»
say &marine.Str;
# OUTPUT: «Sub object coerced to string (please use .gist or .raku to do that)marine»
say &marine.raku; # OUTPUT: «sub marine { #`(Sub|94280758332168) ... }»
method file
method file(Code:D: --> Str:D)
코드 객체가 선언된 파일의 이름을 반환해요.
say &infix:<+>.file; # OUTPUT: «SETTING::src/core.c/Numeric.rakumod»
method line
method line(Code:D: --> Int:D)
코드 객체의 선언이 시작되는 소스 코드의 줄 번호를 반환해요.
say &infix:<+>.line; # OUTPUT: «208»
코드 객체가 자동으로 생성된 것(즉 소스 코드에 선언되지 않은 것)이라면 line은 둘러싸는 스코프의 선언이 시작되는 줄을 반환해요. 예를 들어 has $.name 문법으로 만들어진 자동 생성 접근자 메서드에서 line을 호출하면, 그 메서드가 속한 클래스의 선언이 시작되는 줄을 반환합니다.
예를 들어 이런 소스 파일이 있다면:
class Food { # Line 1
has $.ingredients; # Line 2
# Line 3
method eat {}; # Line 4
} # Line 5
line 메서드는 다음처럼 출력해요.
say Food.^lookup('eat').line; # OUTPUT: «4»
say Food.^lookup('ingredients').line; # OUTPUT: «1»
method bytecode-size
method bytecode-size(--> Int:D)
참고: 이 메서드는 MoarVM 백엔드의 Rakudo 컴파일러에서 2022.06 릴리즈부터 사용할 수 있어요.
코드 객체가 메모리에서 차지하는 바이트 수를 반환해요. 코드 객체가 실제로 multi라면 바이트코드 크기는 proto에 대해 보고된다는 점에 유의하세요. .candidates 메서드로 각 후보를 얻은 다음 각각에 bytecode-size 메서드를 호출할 수 있어요.
say &grep.bytecode-size; # OUTPUT: «114»
say &grep.candidates>>.bytecode-size; # OUTPUT: «424258»
method is-implementation-detail
method is-implementation-detail(--> False)
참고: 이 메서드는 Rakudo 컴파일러에서 2020.05 릴리즈부터 사용할 수 있어요.
코드 객체가 is implementation-detail 트레잇으로 표시됐으면 True, 아니면 False를 반환해요.