Nullable Value Types

Nullable Value Types (Nullable 값 형식)

F#의 Nullable 값 형식(nullable value type)을 설명하는 공식 문서를 한국어로 옮겨 봤어요. 다른 언어를 쓰다 F#에 오면 int 같은 값에 "값이 없을 수도 있다"라는 상태를 어떻게 표현해야 할지 낯설 수 있는데요, 그때 쓰는 도구가 바로 Nullable<'T>예요. 코드와 시그니처는 원문 그대로 보존했으니 눈으로 비교하며 읽어 보세요.

출처: https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/nullable-value-types

본문

널 값 형식 Nullable<'T>는 "null이 될 수도 있는" 모든 struct 형식을 나타내요. 이런 구조는 정수 같은 형식을 효율성(efficiency)을 위해 null로 표현하기도 하는 라이브러리나 컴포넌트와 연동할 때 특히 도움이 돼요. 이 구문의 바탕이 되는 실제 형식은 System.Nullable<T>예요.

구문 (Syntax)

Nullable<'T>
Nullable value

값으로 선언하고 할당하기 (Declare and assign with values)

널 값 형식을 선언하는 건 F#에서 다른 wrapper류 형식을 선언하는 것과 똑같아요.

open System

let x = 12
let nullableX = Nullable<int> x

제네릭 형식 매개 변수를 생략하고 형식 유추(type inference)가 알아서 해결하게 할 수도 있어요.

open System

let x = 12
let nullableX = Nullable x

널 값 형식에 할당할 때도 역시 명시적으로 해 줘야 해요. F#에서 정의한 널 값 형식에는 암시적 변환(implicit conversion)이 없거든요.

open System

let mutable x = Nullable 12
x <- Nullable 13

null 할당하기 (Assign null)

널 값 형식에 null을 직접 할당할 수는 없어요. 대신 Nullable()을 쓰면 돼요.

let mutable a = Nullable 42
a <- Nullable()

그 이유는 Nullable<'T>에는 null이 "제대로 된 값(proper value)"으로 존재하지 않기 때문이에요.

멤버에 전달하고 할당하기 (Pass and assign to members)

F# 값과 멤버(member)를 다룰 때의 중요한 차이점이 하나 있어요. 멤버를 다룰 때는 널 값 형식이 "암시적으로 유추"될 수 있다는 점이에요. 널 값 형식을 입력으로 받는 다음 메서드를 생각해 볼게요.

type C() =
    member _.M(x: Nullable<int>) = x.HasValue
    member val NVT = Nullable 12 with get, set

let c = C()
c.M(12)
c.NVT <- 12

위 예제에서 메서드 M12를 그대로 전달할 수 있고, 자동 속성(property) NVT에도 12를 할당할 수 있어요. 입력이 널 값 형식으로 구성될 수 있고 대상 타입과 맞는다면, F# 컴파일러가 그런 호출이나 할당을 암시적으로 변환해 줘요.

널 값 형식 인스턴스 살펴보기 (Examine a nullable value type instance)

Option이 "값이 있을 수도 있는" 상황을 표현하는 일반화된 구조인 반면, 널 값 형식은 패턴 매칭(pattern matching)과 함께 쓰지 않아요. 대신 if 표현식으로 HasValue 속성을 검사해야 해요.

바탕이 되는 값을 얻고 싶다면 HasValue 검사 뒤에 Value 속성을 사용하면 돼요.

open System

let a = Nullable 42

if a.HasValue then
    printfn $"{a} is {a.Value}"
else
    printfn $"{a} has no value."

Nullable 연산자 (Nullable operators)

널 값 형식에 대한 연산(산술, 비교 등)은 nullable 연산자를 요구할 수 있어요.

FSharp.Linq 네임스페이스의 변환 연산자(conversion operator)를 쓰면 한 널 값 형식에서 다른 널 값 형식으로 변환할 수 있어요.

open System
open FSharp.Linq

let nullableInt = Nullable 10
let nullableFloat = Nullable.float nullableInt

적절한 비-nullable 연산자를 사용해 기본형(primitive type)으로 변환할 수도 있어요. 이 경우 값이 없으면 예외(exception)가 발생할 위험이 있죠.

open System
open FSharp.Linq

let nullableFloat = Nullable 10.0
let standardFloat = float nullableFloat

printfn $"value is %f{float standardFloat}"

nullable 연산자를 HasValueValue를 검사하는 것의 축약형으로 쓸 수도 있어요.

open System
open FSharp.Linq

let nullableInt = Nullable 10
let nullableFloat = Nullable.float nullableInt

let isBigger = nullableFloat ?> 1.0
let isBiggerLongForm = nullableFloat.HasValue && nullableFloat.Value > 1.0

?> 비교는 왼쪽이 nullable인지 확인하고, 값이 있을 때만 성공해요. 이는 바로 뒤에 나온 줄과 같은 의미예요.

더 알아보기 (Learn more)