2.1.5 Nullable(널 허용 여부)

2.1.5 Nullable(널 허용 여부)

정의: nullable — Haxe에서 타입이 nullable이라는 것은 null이 그 타입의 유효한 값이라는 뜻입니다.

프로그래밍 언어는 보통 nullability에 대한 단일하고 명확한 정의를 가지기 마련이에요. 하지만 Haxe는 대상 언어들의 특성 때문에 이 부분에서 절충안을 찾아야 합니다. 어떤 대상 언어는 모든 것에 대해 null을 허용하고(사실 기본값으로 삼고), 다른 언어는 특정 타입에 대해 null조차 허용하지 않기 때문이죠. 이 때문에 두 종류의 대상 언어를 구분할 필요가 있습니다:

정의: 정적 대상(Static target) — 정적 대상은 자체 타입 시스템을 사용하며, 기본 타입에 대해 null이 유효한 값이 아닙니다. Flash, C++, Java, C# 대상이 여기에 해당해요.

정의: 동적 대상(Dynamic target) — 동적 대상은 타입에 더 관대하며 기본 타입에 대해 null 값을 허용합니다. JavaScript, PHP, Neko, Flash 6-8 대상이 여기에 해당해요.

동적 대상에서 null을 다루는 데에는 걱정할 것이 없어요. 하지만 정적 대상에서는 약간의 고려가 필요할 수 있습니다. 우선 기본 타입은 기본값(default values)으로 초기화됩니다.

정의: 기본값(Default values) — 기본 타입은 정적 대상에서 다음과 같은 기본값을 가집니다:

  • Int: 0
  • Float: Flash에서는 NaN, 다른 정적 대상에서는 0.0
  • Bool: false

결과적으로 Haxe 컴파일러는 정적 대상에서 기본 타입에 null을 할당하는 것을 허용하지 않아요. 이를 위해선 기본 타입을 Null<T>로 감싸야 합니다:

// error on static platforms
var a:Int = null;
var b:Null<Int> = null; // allowed

마찬가지로 기본 타입은 감싸지 않는 한 null과 비교할 수 없습니다:

var a : Int = 0;
// error on static platforms
if( a == null ) { ... }
var b : Null<Int> = 0;
if( b != null ) { ... } // allowed

이 제한은 유니피케이션(unification)이 수행되는 모든 상황으로 확장됩니다.

정의: Null<T> — 정적 대상에서 Null<Int>, Null<Float>, Null<Bool> 타입을 사용하면 null을 값으로 허용할 수 있어요. 동적 대상에서는 이 타입이 아무 효과가 없습니다. Null<T>는 다른 타입과 함께 사용해 null이 허용된 값임을 문서화할 수도 있어요.

null 값이 Null<T>Dynamic에 "숨겨져" 있다가 기본 타입에 할당되면, 기본값이 사용됩니다:

var n : Null<Int> = null;
var a : Int = n;
trace(a); // 0 on static platforms

출처: Nullability

본문

정적 대상과 동적 대상의 차이

Haxe는 대상 플랫폼에 따라 null 처리 방식이 크게 달라져요. 정적 대상(Flash, C++, Java, C#)은 자체 타입 시스템에서 기본 타입에 null을 허용하지 않습니다. 반면 동적 대상(JavaScript, PHP, Neko, Flash 6-8)은 타입에 관대해서 기본 타입에 null을 쓸 수 있어요.

이는 Haxe 코드가 플랫폼별로 일관된 null 동작을 보장하기 위해 도입한 설계예요. 개발자가 대상 플랫폼의 차이를 명확히 인지하고 코드를 작성할 수 있도록 도와줍니다.

기본값 규칙

정적 대상에서 기본 타입은 명시적으로 초기화하지 않아도 기본값을 가집니다. Int0, Boolfalse, Float는 대부분 0.0(Flash는 NaN)이에요. 따라서 초기화되지 않은 기본 타입 변수를 읽어도 안전합니다.

Null 래핑

기본 타입에 null을 허용하고 싶다면 Null<T>로 감싸면 돼요. 예를 들어 Null<Int>는 정적 대상에서도 null을 가질 수 있습니다. 이 래핑은 null 허용 여부를 타입 수준에서 명시적으로 표현하는 방법이에요.

숨겨진 null의 처리

처음부터 기본 타입에 null을 넣을 수는 없지만, Null<T>Dynamic에 들어 있던 null이 기본 타입으로 이동하면 기본값으로 대체됩니다. 예제에서 Null<Int> 변수 n에 null이 들어 있고, 이를 Int 변수 a에 할당하면 a는 기본값 0이 돼요.

더 알아보기