Unboxed, Boxed, Unlifted, Lifted

Posted on December 20, 2022

생각 스트레칭

현실에서 상자Box에 담아둔다는 어떤 효과가 생길까요?
사과를 박스에 넣고, 귤을 박스에 넣어 놓는다면, 둘 모두 하나의 이름 Box라고 부를 수 있습니다.
상자가 없는 상태에서는 Int#이지만, 이 걸 Box에 넣고, 때에 따라 Bottom도 넣을 수 있도록 하면 상자안에 Int#이 있거나, 상자 안에 Bottom이 있어도 상자라고 부를 수 있습니다. 이 상자 Int# | BottomInt라고 부릅니다. ※ 무한 루프처럼 값을 돌려주지 않을 때의 반환값을 bottom이라 하니, “bottom이 들어 있다는 잘못된 표현”입니다. 여기서는 결과 형태가 “한 가지”가 아니라는 의미로 썼습니다.

무언가 타입으로 감싼다wrap는 것도, 위와 같은 목표를 포함하고 있습니다. 여러 요소를 묶어 하나로 부를 수 있게, 같은 타입으로 취급할 수 있게 해주는, 즉 추상화를 하는 방법입니다. Just nNothingMaybe로 취급할 수 있습니다. 이런 의미를 알면, Boxed이면 뭔가 추상화 할 것으로 금방 추측 할 수 있습니다.

Boxed

포인터 -----> 힙: Value1 | <thunk>

힙에 있는 객체를 가리키는 포인터입니다. 하스켈은 Lazy 때문에 <thunk>로 머물러 있다가, 평가되면 값으로 바뀌어야 합니다. 적어도 두 가지 형태를 하나로 볼 수 있어야 합니다. 포인터로 두면 <thunk>를 가리키거나 평가된 값을 가리킬 수 있게 됩니다. (<thunk>가 있어야 boxed라고 하는 게 아니라, 힙에 넣어두고 포인팅하기만 하면 boxed입니다.)

예) Int는 2Word 크기의 힙 객체를 가리키는 포인터입니다.

※ unboxed 값 보다는 한 단계 과정을 거쳐야 하니, 당연히 unboxed보다는 퍼포먼스가 좋지 않고, 컴파일러 unboxed 타입으로 바꿀 수 있으면, 컴파일 과정에서 unboxed로 바꿉니다.

Unboxed

Int#

포인터가 아니라, 값 자체로 raw machine 타입입니다.
예) Int#은 C의 long int
Double#은 C의 double
Addr#은 C의 void*
+#은 primitive operation
primitive가 붙은 것들은 하스켈로 코드로 구현되어 있지 않다고 생각하면 됩니다. 런타임 베이스에 들어 있는 타입이나 연산입니다.

하스켈 타입 변수는 <thunk>일 수 있어야 하기 때문에, #이 붙어 있는 unboxed가 될 수 없습니다.
같은 이유로, 폴리모픽 함수에겐 unboxed를 넘길 수 없습니다.

※ 하스켈에선 특별한 기능이 있어서 아니라, 그저 관행적으로 unboxed 타입에는 해시 마크 #을 붙입니다. #이 붙어 있다고 특별한 작업을 하는 것은 아니란 뜻입니다.

Unboxed tuple, Unboxed sums

Lifted

※ Bottom은 값을 반환하지 않는 걸 의미하니, 0 | 1 | Bottom을 쓴다고 Bottom이 어떤 값을 의미하는 건 아닙니다. 그저 선택지 중 하나임을 표시합니다.

포인터 -----> 힙: Value1 | <thunk> | Bottom

Bottom이 될 수 있는 타입을 lifted 타입이라 부르고, 예) 가능한 값0 | 1 | 2 | 3...이 있는 Int# 타입에 Bottom을 추가해서 0 | 1 | 2 | 3...| Bottom이 되는 걸 lifted 되었다고 표현합니다. 포인터로 가리키지 않는, primitive 타입들로는 Bottom을 표현할 수가 없습니다. IntInt# | Bottom를 가리키는 것으로 볼 수 있습니다.

※ 기존 것에 무언가 추가된 요소, 즉 다시 말해, 기존 세계와 비슷하지만 어떤 요소가 추가된 세계로 바뀌었을 때 쓰는 표현입니다. 펑터로 변형되어 추가적인 어떤 요소가 있는 타입으로 바뀌는 걸 lifted 되었다고 말하기도 합니다. 여기 4가지 형태를 거론할 때는 특히 Bottom이 가능하냐 안하냐로 읽으면 무리가 없습니다.

Unlifted

Bottom이 될 수 있는 값으로 lift하지 않은 타입을 unlifted 타입이라 부릅니다. unboxed 타입들, #이 붙은 타입들은 Bottom이 될 수 없으니 모두 다 unlifted입니다.

주의

unlifted라고 다 unboxed는 아니지만, lifted는 모두 다 boxed입니다.

boxed, unboxed 구분은 힙 객체를 가리키는 포인터냐 아니냐,
lifted, unlifted 구분은 Bottom이 될 수 있냐 없냐로 구분합니다.

Array#은 unlifted, boxed 타입입니다. - unlifted이니 Bottom은 될 수 없지만, boxed이니 힙 객체를 가리키는 포인터입니다.
하지만 unboxed이면서 lifted일 순 없습니다. - 포인터가 아니면 Bottom을 가리킬 수가 없습니다.

unboxed를 패턴 매칭하려면, ! bang을 붙여서 정의합니다.

GHC - Unboxed types and primitive operations
FP Complete - Primitive Haskell

Github 계정이 없는 분은 메일로 보내주세요. lionhairdino at gmail.com