현실에서 상자Box에 담아둔다는 어떤 효과가 생길까요?
사과를 박스에 넣고, 귤을 박스에 넣어 놓는다면, 둘 모두 하나의 이름 Box라고 부를 수 있습니다.
상자가 없는 상태에서는 Int#
이지만, 이 걸 Box에 넣고, 때에 따라 Bottom
도 넣을 수 있도록 하면 상자안에 Int#
이 있거나, 상자 안에 Bottom
이 있어도 상자라고 부를 수 있습니다. 이 상자 Int# | Bottom
을 Int
라고 부릅니다.
※ 무한 루프처럼 값을 돌려주지 않을 때의 반환값을 bottom
이라 하니, “bottom
이 들어 있다는 잘못된 표현”입니다. 여기서는 결과 형태가 “한 가지”가 아니라는 의미로 썼습니다.
무언가 타입으로 감싼다wrap는 것도, 위와 같은 목표를 포함하고 있습니다. 여러 요소를 묶어 하나로 부를 수 있게, 같은 타입으로 취급할 수 있게 해주는, 즉 추상화를 하는 방법입니다. Just n
도 Nothing
도 Maybe
로 취급할 수 있습니다. 이런 의미를 알면, Boxed이면 뭔가 추상화 할 것으로 금방 추측 할 수 있습니다.
포인터 -----> 힙: Value1 | <thunk>
힙에 있는 객체를 가리키는 포인터입니다. 하스켈은 Lazy 때문에 <thunk>
로 머물러 있다가, 평가되면 값으로 바뀌어야 합니다. 적어도 두 가지 형태를 하나로 볼 수 있어야 합니다. 포인터로 두면 <thunk>
를 가리키거나 평가된 값
을 가리킬 수 있게 됩니다. (<thunk>
가 있어야 boxed라고 하는 게 아니라, 힙에 넣어두고 포인팅하기만 하면 boxed입니다.)
예) Int
는 2Word 크기의 힙 객체를 가리키는 포인터입니다.
※ unboxed 값 보다는 한 단계 과정을 거쳐야 하니, 당연히 unboxed보다는 퍼포먼스가 좋지 않고, 컴파일러 unboxed 타입으로 바꿀 수 있으면, 컴파일 과정에서 unboxed로 바꿉니다.
Int#
포인터가 아니라, 값 자체로 raw machine 타입입니다.
예) Int#
은 C의 long int
Double#
은 C의 double
Addr#
은 C의 void*
+#
은 primitive operation
primitive가 붙은 것들은 하스켈로 코드로 구현되어 있지 않다고 생각하면 됩니다. 런타임 베이스에 들어 있는 타입이나 연산입니다.
하스켈 타입 변수는 <thunk>
일 수 있어야 하기 때문에, #
이 붙어 있는 unboxed가 될 수 없습니다.
같은 이유로, 폴리모픽 함수에겐 unboxed를 넘길 수 없습니다.
※ 하스켈에선 특별한 기능이 있어서 아니라, 그저 관행적으로 unboxed 타입에는 해시 마크 #
을 붙입니다. #
이 붙어 있다고 특별한 작업을 하는 것은 아니란 뜻입니다.
※ Bottom은 값을 반환하지 않는 걸 의미하니, 0 | 1 | Bottom
을 쓴다고 Bottom
이 어떤 값을 의미하는 건 아닙니다. 그저 선택지 중 하나임을 표시합니다.
포인터 -----> 힙: Value1 | <thunk> | Bottom
Bottom
이 될 수 있는 타입을 lifted 타입이라 부르고,
예) 가능한 값0 | 1 | 2 | 3...
이 있는 Int#
타입에 Bottom
을 추가해서 0 | 1 | 2 | 3...| Bottom
이 되는 걸 lifted 되었다고 표현합니다. 포인터로 가리키지 않는, primitive 타입들로는 Bottom
을 표현할 수가 없습니다. Int
는 Int# | Bottom
를 가리키는 것으로 볼 수 있습니다.
※ 기존 것에 무언가 추가된 요소, 즉 다시 말해, 기존 세계와 비슷하지만 어떤 요소가 추가된 세계로 바뀌었을 때 쓰는 표현입니다. 펑터로 변형되어 추가적인 어떤 요소가 있는 타입으로 바뀌는 걸 lifted 되었다고 말하기도 합니다. 여기 4가지 형태를 거론할 때는 특히 Bottom
이 가능하냐 안하냐로 읽으면 무리가 없습니다.
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