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를 참고해요.

더 알아보기