Nil — 값이 없음을 나타내는 특별한 값
Nil — 값이 없음을 나타내는 특별한 값
보통 값이 들어가야 할 자리를 Nil로 채우면, 그 자리에 값이 없다는 사실을 명시적으로 나타낼 수 있어요. Nil은 Failure보다 더 가볍고 덜 "폭발적인" 대안으로도 쓰여요. (실제로 Failure 클래스는 Nil에서 파생되기 때문에, Nil을 스마트매치하면 Failure에도 맞아요.)
Nil 클래스와 그 유일한 값 Nil은 사실상 같은 것이에요.
say Nil === Nil.new; # OUTPUT: «True»
Failure와 마찬가지로, Nil과 그 서브클래스는 루틴이 특정 반환 타입을 지정하더라도 항상 반환될 수 있어요. 반환 타입의 정의됨(definedness)과 무관하게 반환될 수 있지만, 그 외의 모든 목적에서 Nil은 정의되지 않은 값으로 취급돼요.
sub a( --> Int:D ) { return Nil }
a().say; # OUTPUT: «Nil»
빈 루틴이나 클로저, 혹은 return 문만 있는 루틴이 반환하는 값이 바로 Nil이에요.
sub a { }; a().say; # OUTPUT: «Nil»
sub b { return }; b().say; # OUTPUT: «Nil»
say (if 1 { }); # OUTPUT: «Nil»
{ ; }().say; # OUTPUT: «Nil»
say EVAL ""; # OUTPUT: «Nil»
리스트 안에서 Nil은 값 하나의 자리를 차지해요. Nil을 반복하면 어떤 비반복 가능한 값과 마찬가지로 동작해서, Nil 하나로 이루어진 시퀀스를 만들어요. (다른 의미가 필요할 때는 특별한 값 Empty를 써서 리스트에 들어가면 자리를 차지하지 않고, 반복하면 아무 값도 반환하지 않게 할 수 있어요.)
(1, Nil, 3).elems.say; # OUTPUT: «3»
(for Nil { $_ }).raku.say; # OUTPUT: «(Nil,)»
Nil에 존재하지 않는 메서드를 호출하거나, 어떤 첨자 연산을 해도 성공하고 Nil을 반환해요.
say Nil.ITotallyJustMadeThisUp; # OUTPUT: «Nil»
say (Nil)[100]; # OUTPUT: «Nil»
say (Nil){100}; # OUTPUT: «Nil»
컨테이너에 할당할 때, Nil 값(서브클래스는 제외)은 컨테이너를 기본값으로 되돌리려 시도해요. 기본값이 선언돼 있지 않으면 Raku는 Any를 가정해요.
해시 할당은 두 요소를 기대하기 때문에 Nil이 아니라 Empty를 써야 해요.
my %h = 'a'..'b' Z=> 1..*;
# stuff happens
%h = Empty; # %h = Nil will generate an error
다만 컨테이너 타입이 :D로 제약되어 있으면, Nil을 할당하는 순간 예외가 던져져요. (대조적으로, 인스턴스화된 Failure는 확실한 값이므로 :D와 맞지만, Failure의 부모 클래스가 아닌 한 실제 명목 타입과는 맞지 않을 거예요.) 네이티브 타입은 기본값을 가질 수도 없고 타입 객체를 담을 수도 없어요. 네이티브 타입 컨테이너에 Nil을 할당하면 런타임 오류로 실패해요.
my Int $x = 42;
$x = Nil;
$x.say; # OUTPUT: «(Int)»
sub f( --> Int:D ){ Nil }; # this definedness constraint is ignored
my Int:D $i = f; # this definedness constraint is not ignored, so throws
CATCH { default { put .^name, ': ', .Str } };
# OUTPUT: «X::TypeCheck::Assignment: Type check failed in assignment to $y; expected Int but got Any (Any)»
sub g( --> Int:D ){ fail "oops" }; # this definedness constraint is ignored
my Any:D $h = g; # failure object matches Any:D, so is assigned
하지만,
my Int:D $j = g;
# It will throw both exceptions:
# Earlier failure:
# oops
# in sub g at <unknown file> line 1
# in block <unit> at <unknown file> line 1
#
# Final error:
# Type check failed in assignment to $j; expected Int:D but got Failure (Failure.new(exception...)
# in block <unit> at <unknown file> line 1
타입 없는 변수는 Any 타입이기 때문에, 거기에 Nil을 할당하면 (Any) 타입 객체가 돼요.
my $x = Nil;
$x.say; # OUTPUT: «(Any)»
my Int $y = $x; # will throw an exception
CATCH { default { put .^name, ': ', .Str } };
# OUTPUT: «X::TypeCheck::Assignment: Type check failed in assignment to $y; expected Int but got Any (Any)»
변수가 오른쪽에 나타날 때 객체를 타입 객체로 변환하는 변수를 원한다면, 컨테이너를 Nil 타입으로 지정하면 돼요.
my Nil $x;
my Str $s = $x;
$s.say; # OUTPUT: «(Str)»
이 "타입 객체로의 변환" 규칙에는 중요한 예외가 하나 있어요. 기본값을 가진 변수에 Nil을 할당하면 그 기본값이 복원돼요.
my Int $x is default(42) = -1;
my $y = 1;
for $x, $y -> $val is rw { $val = Nil unless $val > 0 }
$x.say; # OUTPUT: «42»
BIND-POS, ASSIGN-KEY, ASSIGN-POS 같은 메서드는 죽고(die), BIND-KEY는 X::Bind 예외를 담은 failure를 만들며, STORE는 X::Assignment::RO 예외를 만들 거예요.
본문
method append
method append(*@)
Nil(또는 파생 타입 객체)에 append하려 했다고 경고해요.
method gist
method gist(--> Str:D)
"Nil"을 반환해요.
method Str
method Str()
Nil을 문자열로 만들려 했다고 경고해요.
method new
method new(*@)
Nil을 반환해요.
method prepend
method prepend(*@)
Nil이나 파생 타입 객체에 prepend하려 했다고 경고해요.
method push
method push(*@)
Nil이나 파생 타입 객체에 push하려 했다고 경고해요.
method unshift
method unshift(*@)
Nil이나 파생 타입 객체에 unshift하려 했다고 경고해요.
method ords
빈 Seq를 반환하지만, 사용되는 컨텍스트에 따라 경고도 함께 냅니다(예: say와 함께 쓰면 문자열 컨텍스트에서 사용한다는 경고).
method chrs
\0을 반환하고, 경고도 던져요.
method FALLBACK
method FALLBACK(| --> Nil) {}
fallback 메서드 는 어떤 인자든 받아서 항상 Nil을 반환해요.
method Numeric
method Numeric()
Nil을 숫자로 만들려 했다고 경고해요.