DEV Community

Ingun 전인건
Ingun 전인건

Posted on

Monad가 Monoid인 이유 수학 없이 이해하기

Motivation

저는 처음 모나드/모노이드를 배울때 누군가가 “모나드는 모노이드일 뿐이야” 라고 설명해줬으면 얼마나 좋았을까 생각해요. 그걸 알았다면 두개를 따로 따로 배우느라 시간낭비 하지 않았을텐데 하는 아쉬움이있어요. 생각해보니 대부분의 프로그래머를 위한 Monad 와 Monoid 학습 자료들을 보면 Monad의 경우 업계에서 Monad란 무엇인가?! 라는 주제의 글을 금지하고 있을정도로 깊이있는 탐구보단 프로그래머관점에서의 표면적인 이해를 위한 자료가 많아요. Monoid 도 마찬가지죠. 물론 프로그래머로써 이 둘을 깊게 이해할 필요는 없어요. 그 때문인지 이 둘이 독립적으로 다뤄질 때가 많아요.

이 글에서는 최대한 프로그래머 관점에서 쉽게 모나드를 모노이드로 설명하고, 모노이드로 정의를 내려봄으로써 독자가 이 두 토픽을 확실하게 이해할 수 있게끔 노력해볼거에요.

들어가기 앞서 둘이 같다는걸 이해하는것은 전혀 어렵지 않고 손뻗으면 닿는, 바로 눈앞에있다는것을 먼저 보여드릴게요

우선 모나드와 모노이드의 문서상 정의부터 비교해보죠.

Monadμ::T2Tη::ITμ(T,μ(T2))=μ(μ(T2),T)μ(η(I),T)=μ(T,η(I))=TMonoid::T×TTit1(t2t3)=(t1t2)t3it=ti=t \begin{aligned} &\text{Monad} \\ &\mu :: T^2 \rightarrow T \\ &\eta :: I \rightarrow T \\ &\mu(T,\mu(T^2)) = \mu(\mu(T^2),T) \\ &\mu(\eta(I),T) = \mu(T,\eta(I)) = T \\ \end{aligned} \hspace{1em} \begin{aligned} &\text{Monoid} \\ &\otimes :: T \times T \rightarrow T \\ &i \\ &t_1 \otimes (t_2 \otimes t_3) = (t_1 \otimes t_2) \otimes t_3 \\ &i \otimes t = t \otimes i = t \\ \end{aligned}

각 수식의 정확한 의미는 몰라도 굉장히 비슷한게 보이시나요?

각도를 다르게 해서 봐보죠.

Monad1ηTT2μTT×1Tid×ηT2μTη×id1×TTT3id×μT2μ×idμT2μT \text{Monad} \\ \text{}\\ \begin{gathered} 1 \\ \darr_{\eta} \\ T \\ \end{gathered} \hspace{1em} \begin{gathered} T^2 \\ \darr_{\mu} \\ T \\ \end{gathered} \hspace{1em} \begin{gathered} T \times 1 \\ \darr \\ T \\ \end{gathered} \begin{gathered} \xrightarrow{id\times\eta} \\ \\ \rightarrow \\ \end{gathered} \begin{gathered} T^2 \\ \darr_{\mu} \\ T \\ \end{gathered} \begin{gathered} \xleftarrow{\eta\times id} \\ \\ \leftarrow \\ \end{gathered} \begin{gathered} 1 \times T \\ \darr \\ T \\ \end{gathered} \hspace{1em} \begin{gathered} T^3 \\ \darr_{id \times \mu} \\ T^2 \\ \end{gathered} \begin{gathered} \xrightarrow{\mu\times id} \\ \\ \xrightarrow{\mu} \\ \end{gathered} \begin{gathered} T^2 \\ \darr_{\mu} \\ T \\ \end{gathered}
Monoid1idTT×TTT×1T1×idT×TTid×11×TTT×T×T1×T×T×1T×TT \text{Monoid} \\ \text{}\\ \begin{gathered} 1 \\ \darr_{id} \\ T \\ \end{gathered} \hspace{1em} \begin{gathered} T\times T \\ \darr_{\otimes} \\ T \\ \end{gathered} \hspace{1em} \begin{gathered} T \times 1 \\ \darr \\ T \\ \end{gathered} \begin{gathered} \xrightarrow{1\times id} \\ \\ \rightarrow \\ \end{gathered} \begin{gathered} T \times T \\ \darr_{\otimes} \\ T \\ \end{gathered} \begin{gathered} \xleftarrow{id \times 1} \\ \\ \leftarrow \\ \end{gathered} \begin{gathered} 1 \times T \\ \darr \\ T \\ \end{gathered} \hspace{1em} \begin{gathered} T\times T\times T \\ \darr_{1 \times \otimes} \\ T\times T \\ \end{gathered} \begin{gathered} \xrightarrow{\otimes\times 1} \\ \\ \xrightarrow{\otimes} \\ \end{gathered} \begin{gathered} T\times T \\ \darr_{\otimes} \\ T \\ \end{gathered}

모양이 비슷하죠? 자세하게는 몰라도 둘이 같은걸 뜻한다는 느낌을 받을거에요.
모나드의 각 property들이 모노이드에 어떻게 대응하는지만 이해한다면 둘이 같은걸로 보이게 될거에요.

Monad <-> Monoid

프로그래머한테 모나드 T의 정의는 다음과같은 signature를 가진 두 함수의 존재에요.

μ(T2)Tη(I)T \mu(T^2) \rightarrow T \newline \eta(I) \rightarrow T

프로그래머한테는 보통 μ\mu join으로, η\eta return으로 많이 알려져 있죠. TT 는 모나드 타입이고 T2T^2 는 모나드 타입 TT 를 두번 적용 한 거에요. 모나드 List TT 라면 ListList T2T^2 가 되겠죠.

물론 signature가 똑같다고 아무 함수나 되는건 아니에요. 만족해야하는 조건이 있어요. 하지만 이 조건을 컴파일러 레벨에서 강제할 수 있는 언어가 (제가알기론)없다보니 이름하야 monadic law는 프로그래머들에겐 무시당할 때가 많죠. 하지만 모나드가 모노이드라는 사실을 이해하기위해선 Monadic law 도 이해해야해요. 왜냐하면 Monadic Law는 두가지 (join의 룰, return의 룰)이 있는데 이 각각이 모노이드의 Associativity 와 Identity 에 대응이 되기 때문이에요.

모나드의 Join <-> 모노이드의 Associativity

μ\mu (혹은 join) 이 지켜야 하는 룰은 다음과 같아요

μ(T,μ(T2))=μ(μ(T2),T) \mu(T,\mu(T^2)) = \mu(\mu(T^2),T)

모나드 타입 ( T3T^3 )를 T로 두번 μ\mu 한다고 할때 μ(T2,T)\mu(T^2, T) 를 먼저 하든 μ(T,T2)\mu(T,T^2) 를 먼저 하든 결과는 똑같아야 한다는 뜻이에요. 다시 List로 예를 든다면 List<List<List>>.join().join()의 결과와 List<List<List>>.map(join).join()의 결과는 언제나 같아야 한다는 뜻이에요.

이 룰이 다음과같은 Monoid의 Associativity 에 대응해요.

(t1t2)t3=t1(t2t3) (t_1 \otimes t_2) \otimes t_3 = t_1 \otimes (t_2 \otimes t_3)

그림으로 표현해보면 더 와닿을거에요

Alt Text

Monad의 μ\mu 가 operator역할로 Monoid로써 Associativity 를 만족시켜줘요.

모나드의 세 T는 줄서서 하나의 타입으로 evaluate되길 기다리고 있어요. 양쪽 어느 쌍이 먼저 evaluate 되든 상관없어요 왜냐하면 monadic law 에 의해 결과는 똑같을것이기 때문이죠. 이 모나드의 property 는 모노이드의 associativity 와 대응돼요. 모노이드의 세 T는 줄서서 다른 algebraic value 로 evaluate 되길 기다리고있죠. 모노이드 역시 양쪽 어느 쌍이 먼저 evaluate 되든 상관없어요. 모노이드기 때문이죠.

모나드의 Return <-> 모노이드의 Identity

η\eta (혹은 return) 가 지켜야 하는 룰은 다음과 같아요

μ(η(I),T)=μ(T,η(I)) \mu(\eta(I),T) = \mu(T,\eta(I))

η\eta 로 만든 T의 T든, T의 η\eta 로 만든 T든 μ\mu 를 적용했을 때의 결과값은 똑같아야 한다는 뜻이에요. 다시 List로 예를들면 List.map(return).join()의 결과와 return(List).join()의 결과는 같아야 한다는 뜻이에요.

이 룰은 Monoid의 Identity 에 대응해요

it=ti=t i \otimes t = t \otimes i = t

이것도 그림으로 보면 더 와닿을거에요.

Alt Text

η\eta 도 Monoid로써 Identity를 만족시켜줘요.

근데 μ\mu 가 대응되는 점과 비교해봤을때 이상한 점이 있죠. μ\mu 도 함수고 \otimes 도 함수니깐 짝궁이 맞다고 쳐도 η\eta 는 함수인데 Identity는 오브젝트잖아요!? 이점에대해서 설명하자면 η\eta 는 정확히는 모노이드의 오브젝트 x에 Identity 오브젝트를 곱해주는 (함수로 정의될 수 있는)행위 와 대응이 되는거에요. 즉 함수와 함수끼리 대응되는거라고 볼 수 있죠.

마무리

결론은 모나드는 파라메터 한개짜리 제네릭 타입의 모노이드에요. 이 글이 모나드/모노이드의 시야를 넓히는데 도움이 됐길 바래요.

이 토픽에 대한 더 자세하게 공부하고 싶다면 Category Theory for Programmer - Monads Categorically 를 추천해요.
전반적인 카테고리 이론에 대해 공부하고 싶다면
Category Theory for Programmers
Math3ma - Category Theory
개인적으로 푼 Category Theory for Programmers각 챕터 Challenges의 솔루션: ingun37.github.io/answers/

Discussion (0)