Reference Cells
Reference Cells (참조 셀)
F#에서는 값이 기본적으로 불변(immutable)이에요. 그런데 가끔은 값을 바꿔야 할 때가 있죠. 그럴 때 쓰는 도구가 바로 reference cell(참조 셀) 이에요. 참조 셀은 참조 의미론(reference semantics)을 가진 변경 가능한 값(mutable value) 을 만들어 주는 저장 위치랍니다. 이번 글에서는 참조 셀이 뭔지, 어떻게 만들고 어떻게 다루는지 하나씩 살펴볼게요.
출처
본문
구문(Syntax)
ref expression
설명(Remarks)
ref 함수로 초기 값을 넣어 새 참조 셀을 만들면, 그 안의 값이 mutable이라서 나중에 바꿀 수 있어요. 참조 셀이 주소만 담고 있는 게 아니라 실제 값을 들고 있다는 점이 핵심이에요.
아래 코드가 참조 셀을 선언하고 사용하는 방법을 보여줘요.
let xRef = ref 10
printfn "%d" xRef.Value
xRef.Value <- 11
printfn "%d" xRef.Value
출력은 이렇게 나와요.
10
11
참조 셀은 사실 제네릭 레코드 타입 Ref의 인스턴스예요. 그 선언은 다음과 같죠.
type Ref<'a> =
{ mutable contents: 'a }
'a ref 타입은 Ref의 동의어(시노님)예요. 컴파일러와 IDE의 IntelliSense는 이 타입을 보여줄 때 'a ref를 표시하지만, 실제 정의는 Ref 쪽이에요.
ref 연산자는 새 참조 셀을 만들어 줍니다. 아래 코드가 ref 연산자의 선언이에요.
let ref x = { contents = x }
다음 표는 참조 셀에서 사용할 수 있는 기능들을 정리한 거예요.
| 연산자·멤버·필드 | 설명 | 타입 | 정의 |
|---|---|---|---|
ref (연산자) |
값을 새 참조 셀에 담아요. | 'a -> 'a ref |
let ref x = { contents = x } |
Value (속성) |
바탕이 되는 값을 읽거나 설정해요. | unit -> 'a |
member x.Value = x.contents |
C# 개발자라면 헷갈리기 쉬운데, C#의 ref와 F#의 ref는 다른 개념이에요. F#에서 C#의 ref에 해당하는 건 byrefs인데, 이는 참조 셀과는 별개의 개념이에요.
또한 mutable로 표시된 값이 클로저(closure)에 포착되면 자동으로 'a ref로 승격(promote)될 수 있어요. 자세한 내용은 Values를 참고해요.
더 이상 쓰지 않는 구조(Deprecated constructs)
F# 6.0부터 아래 연산자들은 더 이상 권장되지 않으며, 사용하면 정보용 경고(informational warning)가 나와요.
| 연산자·멤버·필드 | 설명 | 타입 | 정의 |
|---|---|---|---|
! (역참조 연산자, deprecated) |
바탕이 되는 값을 돌려줘요. | 'a ref -> 'a |
let (!) r = r.contents |
:= (대입 연산자, deprecated) |
바탕이 되는 값을 바꿔요. | 'a ref -> 'a -> unit |
let (:=) r x = r.contents |
contents (레코드 필드) |
바탕이 되는 값을 읽거나 설정해요. | 'a |
let ref x = { contents = x } |
이들 대신 .Value를 직접 쓰는 게 권장돼요. 관련 내용은 F# RFC FS-1111에서 확인할 수 있어요.
contents 필드는 다른 버전의 ML과의 호환성을 위해 제공되는데, 컴파일 과정에서 경고가 나와요. 이 경고를 끄려면 --mlcompatibility 컴파일러 옵션을 사용하면 돼요. 자세한 내용은 Compiler Options를 참고해요.