펑터Functor

lionhairdino @lionhairdino@hackers.pub
하스켈에서 펑터를 쓰고 있는 입문자분들과 떠들 내용입니다.
다음처럼 얘기하곤 합니다.
"Maybe Int
타입의 값에서 Int
를 꺼내 올 순 없다."
Maybe
의 fmap
은 패턴 매칭으로 Just 1
을 잡아내어 1
에 접근하기도 하고, fromJust
등으로 Just
가 감싸고 있는 값을 꺼내기도 하는데 이게 무슨 말일까요? 혼자 상상해봤습니다.
올라가면 반드시 내려오는 게 정해진, 경우의 수가 없는 미끄럼틀이 있는데, 내려오는 건 볼 수가 없습니다. 그럼 미끄럼을 내려오는 걸, 미끄럼에 올라 가는 것으로 "표현"할 수 있습니다. 올라 가면 내려오는 이외의 일은 일어나지 않으니까요.
※ 구성원들끼리 어떤 관계를 가지고 있냐를 구조라 부릅니다.
Int
세계에서 이런 구조가 있습니다.
1 ---- (+1) ----> 2
이런 구조를 그대로 살려 Maybe Int
도 같은 구조를 포함하고 있도록 만들고 싶습니다. (펑터는 닮은 것들 끼리 이야기입니다.)
Just 1 ---- (+1) ----> Just 2
Just 1
이 Maybe Int
타입의 "값 자체"는 아닙니다. 1
을 받아 Maybe Int
타입 값을 생성하는 생성식입니다. 미끄럼틀에 올라가는 것과 비슷합니다. 내려오는 것은 보이지 않으니, 올라가는 것으로 지칭하는 것과 비슷합니다.
Just 1
이 Just 2
와 fmap (+1)
관계에 있도록 하려면, Just 1
과 대응되는 원본 1
과 (+1)
관계에 있는 2
에 대응하는 Just 2
를 고르고, 그 둘을 관계짓도록 fmap (+1)
을 정의하면 됩니다. 여기서 1
과 2
는 Maybe Int
에서 빼내온 것이 아닙니다 새로운 세상의 것들과 연관되는 원본들의 포인트를 가져온 것 뿐입니다.
1등만 기억하는 더러운 함수를 만들어 보겠습니다.
func :: Int -> Bool
func 1 = True
func _ = False
일괄적인 어떤 "규칙"을 찾기 보다(분명 사회에는 복잡한 이유가 있겠지만), 여기서는 단순 대응, 매핑, 연관 짓기로 보겠습니다. 마찬가지로 1
과 Just 1
은 연관을 지어놨을 뿐, Just 1
이 생성한 값의 모양은 해
일지 달
일지 뭘로 정했을 지 알 수 없습니다. 아니 알 필요가 없습니다. 우리가 원한 건 원본들의 관계, 즉 구조가 그대로 유지되는 것을 원하니, 그 것만 되고 있으면 됩니다.
fmap
은 Maybe Int
타입 값 안의 Int
를 보고 있는 것이 아닙니다.
펑터를 상자로 보고, 상자 안에서 꺼내오는 것으로 인식해도 많은 경우 무리가 없습니다. (대부분의 펑터 설명 그림들이 상자를 은유로 삼습니다.) 그런데, 왜 이렇게 까다롭게 말장난처럼 하냐면, 가끔 상자에 넣어 놨으니, 필요할 때 꺼내면 된다는 메타포가 맞지 않는 경우가 더러 생깁니다.
예를 들면, 상자 메타포가 맞아 떨어지려면, 상자안에 Int
를 집어 넣어, 상자를 다루는 함수들을 거친 후 나온 최종 상자에서 Int
를 꺼낼 수 있어야 합니다. 하지만, 상자 자체가 사라진 것 같은 결과가 나올 수도 있고, 전혀 Int
와 관계 없어 보이는 것들이 나올 수도 있습니다.
※ 값의 성격은 값 모양으로 정해지는 것이 아니라, 값을 다루는 함수들의 동작에 따라 성격이 정해집니다.
없을 수도 있는 수를 꺼낸다
는 말이, 말이 안된다는 걸 알기가 은근 어렵습니다.