Nil — 값이 없음을 나타내는 특별한 값

Nil — 값이 없음을 나타내는 특별한 값

보통 값이 들어가야 할 자리를 Nil로 채우면, 그 자리에 값이 없다는 사실을 명시적으로 나타낼 수 있어요. NilFailure보다 더 가볍고 덜 "폭발적인" 대안으로도 쓰여요. (실제로 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-KEYX::Bind 예외를 담은 failure를 만들며, STOREX::Assignment::RO 예외를 만들 거예요.

출처: Raku Documentation — Nil

본문

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을 숫자로 만들려 했다고 경고해요.