모나드가 왜 ‘어렵다’고 느껴지는지의 이유를 짚고, 비유에 매달리기보다 다양한 모나드를 직접 쓰고 익히는 연습이 가장 빠른 길임을 강조한다. 자유 모나드 등 아이디어를 예고편처럼 간단히 소개하며, 궁극적으로는 “모나드는 모나드다 — 코드를 쓰자”라는 메시지를 전한다.
세상에는 셀 수 없이 많은 모나드 튜토리얼이 있고, 각각이 모나드는 코끼리다라거나, 아니 접붙이기다라거나, 프로그래머블 컨테이너다라거나, 프로그래밍 가능한 세미콜론이다라거나, 별별 설명이 다 있다. ‘모나드 튜토리얼’로 검색하면 다른 그림 설명이 붙은 온갖 잡다한 모나드들이 잔뜩 걸려온다. 그래, 모나드는 단지 자기 함자들의 범주에서의 모노이드 대상일 뿐이야. 문제라도? 같은 것도 있었지.
이 글의 목적은 이런 기존 모나드 튜토리얼들을 두고 “틀렸어!”라거나 “그걸로 이해할 리가 없잖아!” 하고 깎아내리려는 게 아니다. 실제로 이미 수많이 쓰인 튜토리얼 중에는 내가 하고 싶은 말과 거의 같은 얘기를 하는 것들도 많다. 그렇다면 위의 길고 긴 목록 끝에 또 하나의 ‘알기 쉬운 비유’를 보태려는 것이냐 하면, 그것도 아니다. 아, 그래, 모나드는 비유가 아니다라는 튜토리얼도 있었다. 그럼 왜 이 글을 쓰냐 하면, 이렇게 훌륭한 튜토리얼이 넘쳐나는데도 왜 모나드는 어렵다고 여겨지고 두려워하는지를 밝히고 싶기 때문이다. 그래서, 이 글만 읽어서는 아마 모나드를 이해하는 것은 절대로 불가능할 것이다1. 하지만 모나드를 ‘알게’ 되려면 어떻게 해야 하는지, 그 발판 정도라도 제공할 수 있다면 다행이다.
그건 그렇고, 잠깐 초등학교 때를 떠올려 보자. 산수 시간. 우리는 덧셈과 곱셈을 꽤 오랜 시간에 걸쳐 배웠다. 예를 들어 ‘1+1=2 1 + 1 = 2’는 이런 식으로 배웠다:
문: 1+1=?1+1=?
답: 음, 사과 한 개와 사과 한 개를 합치면……
두 개!
그럼 ‘2×3=6 2 \times 3 = 6’은 이런 식이다:
답: 음, 사과를 한 줄에 두 개씩, 모두 세 줄로 늘어놓으면 되니까……
여섯 개!
계속 가 보자. 5−3=2 5-3=2.
답: 사과 다섯 개에서 세 개를 집어먹으면……
두 개!
이런 식으로 우리는 초등학교에서 계산을 몸에 익히고, 중학교에 들어간다. 중학교에 가자마자 곧바로 다음 같은 문제를 만나 당황하게 된다.
문: 3−5=?3 - 5 = ?
사과 세 개에서 사과 다섯 개를…… 뺄 수가 없잖아!?
여기서 떡 하니 ‘음수’가 등장하고, 우리는 뺄셈을 할 수 있게 된다. 그때 수직선이라는 게 나오고,
0에서 시작해 오른쪽으로 세 칸, 왼쪽으로 다섯 칸 간 위치. 즉 답은 −2-2!
같은 식으로, ‘수직선을 어느 쪽으로 가느냐’라는 직관을 바탕으로 음수의 연산을 익힌다. 혹은, ‘지갑에 300엔밖에 없는데 500엔짜리 과자를 사려면 200엔을 누군가에게 빚져야 한다’ 같은 빚의 메타포로 이해하기도 한다.
이렇게 하면, 곱셈도 할 수 있다. (−2)×3=−6(-2) \times 3 = -6.
‘마이너스 곱하기 마이너스는 어떻게 해야 하지?’라는 때에는, 이번엔 자동차의 속도를 말한다고 생각하면 된다. 그러면 (−2)×(−3)=6(-2) \times (-3) = 6은,
시속 2 2 km로 후진하는 차가 세 시간 전에 있었던 위치는…… 6 6 km 전방!
이런 식으로 계산할 수 있게 된다.
……응, 하고 싶은 말은 알겠다. 이 글은 산수 공부 글이 아니라 모나드 튜토리얼일 터였다.
|l、{ j} /,,ィ//| / ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
i|:!ヾ、ノ/ u {:}//ヘ | 아… 방금 막 일어난 일을 있는 그대로 말하겠다!
|リ u’ } ,ノ ,!V,ハ | < ‘나는 모나드 튜토리얼을 읽고 있다고
fト、{ル{,ィ’eラ , タ人. | 생각했는데 어느새 산수 공부를 하고 있었다’
ヾ|宀| {´,)⌒`/ |<ヽトiゝ | 타입 클래스니 범주론이니
ヽ iLレ u’ | | ヾlトハ〉. | 그런 잔챙이가 전혀 아니었고
ハ !ニ⊇ ‘/:} V:::::ヽ. │ 더 무시무시한 무언가의 편린을 맛보았지…
/:::丶’T’’ /u’ _ /:::::::/`ヽ \____________________2
그렇다. 하지만 이제부터 제대로 모나드 튜토리얼이 될 예정이니 안심하자.
그럼 왜 산수 얘기를 했느냐? 이런 글을 읽고 있을 정도라면, 아마 위에서 했던 산수는 누워서 떡 먹기일 것이다. 1+1 1 + 1 이라든가 (−2)×(−3)(-2) \times (-3) 같은 건 순식간에 계산하겠지. 그렇다면 그 누워서 떡 먹기 같은 계산을 할 때, 위에서 했듯이 일일이 사과나 수직선을 떠올리면서 계산하고 있을까?
아마 답은 ‘아니오’일 것이다. 초등학교나 중학교에서 배운 초기에는 위에 그린 것 같은 그림을 머릿속에 떠올리거나 손가락을 접어가며 열심히 헤아려 답을 냈을 것이다. 하지만 지금은 그렇지 않다. 왜일까?
그건, 여러 번 계산해 익숙해졌기 때문이다. 초등학교 때는 숙제로 내리 연산 드릴이 나오고, 곱셈에 이르러서는 구구단을 외울 때까지 암송 테스트를 한다. 내가 초등학생이었을 때 마침 ‘백칸 계산’ 같은 게 유행해서, 10 × 10 칸을 그저그렇게 채웠다. 그런 식으로, 우리는 처음에 주어진 사과니 속도니 직선 위 거리니 같은 의미를 잊고 숫자 계산을 할 수 있게 되었다.
물론 산수나 수학을 공부하는 데 앞 장에서 했던 것처럼 구체적 이미지를 갖는 것은 어느 정도 의미가 있고 이해에도 도움이 된다. 하지만 사과의 이미지는 음수가 도입될 때 도리어 이해를 방해하고, 빚의 이미지는 음수끼리의 곱셈에는 잘 맞지 않는다. 더 나아가 고등학교에 가서 허수가 도입되면, 직선의 아날로지는 전혀 통하지 않게 된다. 그렇게 ‘0보다 작다니 그게 뭐야……’라거나, ‘제곱해서 음수가 되는 그런 수는 없잖아……’ 같은 말을 하며 마지못해 계산하는 동안 어찌저찌 계산이 되게 된다.
나는 모나드에도 같은 말을 할 수 있다고 본다. 세상에는 여러 비유(브리토, 컨베이어벨트, 상자, 등등)를 사용한 모나드 설명이 있다. 각각 모나드에 대한 영감을 주고 이해에 보조가 되기도 한다. 하지만 하나의 이미지에만 의존해선 이해할 수 없는 것도 많다. IO 모나드는 확실히 컨베이어벨트 같은 느낌이 들기도 한다. 하지만 리스트 모나드가 어떤 면에서 컨베이어벨트란 말인가? 반대로 리스트는 프로그래머블 컨테이너일 수 있겠지만, IO의 어느 부분이 컨테이너일까? 물론 각각은 그럴듯한 방식으로 ‘봐, 이건 컨베이어벨트지’라거나 ‘컨테이너지’라고 설명할 수 있을 것이다. 하지만 설명이 필요한 시점에서, 그 이미지만으로는 모나드를 다 설명할 수 없다는 뜻이 아닐까.
이는 ‘이미지에 의존한 설명을 쓰지 말라’는 비난이 아니다. 거듭 말했듯, 몇 가지 모나드 예시를 이해해 가는 데에는 그런 설명이 매우 유용하다. 하지만 수와 사과를 대응시키고 있으면 음수를 받아들이지 못하는 것처럼, 이미지는 때로 적용 범위를 넘어선 이해를 오히려 저해하기도 한다. 그래서 모나드를 이해하고 싶다면 그런 이미지의 도움을 빌리면서도, 연산 연습처럼 여러 모나드를 사용한 코드를 써 보는 것이 그만큼, 아니 그 이상으로 중요하다.
그럼, ‘모나드는 도대체 뭐야?’라고 물을 수 있다. 어려운 질문이다. 그래도 간단한 답을 찾을 수는 있다:
모나드는, 모나드다.
……이건 아무 말도 아니다. 맞다. 그래서 마침 계산기 과학자가 지나가길래 물어보자. 모나드가 뭐죠?
모나드는 자기 함자들의 범주에서의 모노이드 대상일 뿐이야. 문제라도?
정확히 맞다. 하지만 무슨 소리야? 장난하나? 싶은 기분이다. 곤란하네. 그래서 트위터에 있던 좀 아는 사람에게 물어보았다.
타입
m : * → *와 합성 연산(>=>) :: (a → m b) → (b → m c) → a → m c그리고 함수return :: a → m a의 조합이 모나드라는 것은 다음 법칙을 만족하는 것을 말한다:
f >=> (g >=> h) == (f >=> g) >=> hf >=> return == freturn >=> f == f
……그래서 그게 뭔데? 결국 모나드가 뭔데! ι(`ロ´)ノ
무리가 아니다. 왜냐하면, ‘덧셈이란 뭐지?’라고 물으면, 어떻게 답하겠는가? ‘덧셈은…… 덧셈이지’라고밖에 답할 수 없지 않을까? 곤란해하며 수학자에게 물어보면, ‘무슨 덧셈이냐에 다르지만’이라는 전제를 달고 다음 ‘아벨 군의 공리’를 말할 것이다.
연산 ++가 덧셈이라는 것은, 어떤 특별한 수 0 0가 있어서 다음 법칙을 만족하는 것을 말한다:
- a+(b+c)=(a+b)+c a + (b + c) = (a + b) + c
- a+0=a a + 0 = a
- 0+a=a 0 + a = a
- 모든 a a에 대해 (−a)(-a)라는 수가 있어서, a+(−a)=(−a)+a=0 a + (-a) = (-a) + a = 0
- a+b=b+a a + b = b + a
하지만 이런 말을 들어도, 전혀 덧셈이 무엇인지 알게 된 기분은 들지 않는다. 그래도 그런 것과 상관없이 우리는 날마다 덧셈을 하고, 필요하면 곱셈이나 나눗셈도 한다. 그것도, ‘열 명이 등산을 갔는데 다섯 명만 돌아온……’이라든가, ‘연회, 열 명으로 예약했는데 스무 명은 올 것 같아:;(∩´﹏`∩);:’이라든가, ‘아— 오늘 연회 돈이 부족하니 저놈에게서 1000엔 빌려야지’, ‘두 개씩 기념품을 사니까 전원 합쳐 열 개네—’ 같은, 여러 장면 여러 국면에서 계산을 한다. 이들은 다 의미는 조금씩 다르지만 같은 덧셈과 곱셈을, 우리는 불편함 없이 적용하고 사용하고, 덧셈을 아는 것 같은 기분으로 쓴다.
즉, ‘모나드란 무엇인가’라는 전문적 답을 이해하지 못해도, ‘모나드를 어떻게 쓰는가’라는 실용적 답만 알면, 우리는 그것을 쓸 수 있다. 그렇게 쓰는 동안 ‘어쩐지 나, 모나드를 알겠어’라는 기분이 들어 종횡무진으로 능숙하게 쓰게 된다. ‘모나드는 무엇인가’를 모르면서 ‘모나드는 모나드다’라는 설명을 언젠가 자연스레 받아들이고 쓴다. 그것이 프로그래밍에서 ‘모나드를 이해한다’는 것이다.
그러니 거듭이 되지만, 프로그래밍에서의 모나드를 이해하고 싶다면, 튜토리얼을 한 손에 들고 시행착오하며, 어쨌든 코드를 많이 읽고 쓰는 게 가장 빠른 지름길이라 본다. 산수 공부도 그랬잖나. OOP에서 온 사람도, 클래스, 오브젝트, 상속, 다형성 같은 개념을 입문서의 설명만 읽고 이해해 버리는 사람은 드물 것이다. 여러 프로그램을 쓰는 사이에 익힌 게 아니었을까. 초보일 때는 for 문이나 if 문조차 수십 번 써보고 나서야 겨우 쓰는 법을 익히기도 한다. 그러니 모나드도 같다. 그를 위해 필요한 모나드 튜토리얼은 세상에 잔뜩 있다. 그런 튜토리얼을 발판 삼아, ‘모나드는 모나드다’라고 말할 수 있을 때까지 이것저것 코드를 써 보자. 그것이 모나드를 이해하는 유일한 길이다.
……‘그럼 수학적으로 모나드는 어떤 개념인가?’라고? 어서 오라! 부디 범주론을 공부해 보자. 나도 지금 딱 공부 중이다. 범주론은 수학의 다양한 추상화 방법이 담긴 도구상자다. 그 전부가 프로그래밍에 응용되는 것은 아니지만, 알아 두면 반드시 플러스다. 그렇다 해도 범주론의 논의는 매우 추상적이어서, 어느 정도 수학의 기초적 논의와 집합·논리의 언어에 익숙해지지 않으면 자주 넘어지게 된다. 프로그래밍에 쓰일 부분만 집어먹고 싶다는 마음도 이해한다. 하지만 기본을 잡지 않고 곧바로 범주론에 뛰어드는 것은, 카레도 고추도 먹어 본 적이 없는 사람이 하바네로를 바로 먹는 것과 같다. 어디까지나 한 예지만, 다음 같은 책들을 읽어 보면 좋겠다.
유키 히로시, 『수학걸』 시리즈 수학의 재미와 방법론을 가벼운 소설 형식에 실어 주인공들과 함께 성장하며 배울 수 있는 좋은 책. 집합과 논리의 기초를 제대로 깔아 주기보다는, 이 책을 통해 ‘수학하는 법’을 몸에 붙이는 용도로 좋을 것 같다. 나카우치 노부미쓰, 『수학의 기초 체력을 기르기 위한 논리의 연습장』 수학의 논리와 집합의 기초를, 공부하는 마음가짐부터 차근차근 설명한 책. ‘논리(ろんり)’라는 말은 저자의 용어로, 수학의 기초 체력으로서의 집합·논리를 뜻한다. 저자 특유의 감각이 빛나는 말장난이 곳곳에 흩뿌려져 있어 부담 없이 독학할 수 있다. 나는 중학생 때 이 책으로 공부했다. 또, 미독이지만 같은 저자의 『논리와 집합』도 좋은 책인 듯하다. 가다 마사루, 『논리와 집합에서 시작하는 수학의 기초』 집합과 논리의 쓰임을 아주 친절하게 소개하는 좋은 책이라고 한다. ‘라고 한다’인 이유는 내가 아직 읽지 않았기 때문이지만, 평판이 아주 좋은 책이니 안심하고 추천할 수 있을 듯.
이런 책들로 준비운동을 하고 범주론을 공부해 나가면, 위에서 언급했던 ‘모나드의 법칙’ 그 자체가 곧 모나드라는 것을 알게 될 것이다. 모나드 법칙만 줄줄이 늘어놔도 잘 모르겠지만, 중요한 것은 ‘모나드를 쓰면 부작용의 대부분을 통일적으로 다룰 수 있다’는 점이다. 모나드를 비롯해 수학에서의 대수적 구조라는 것은 그 계산 법칙 자체가 그 의미라는 사실은 아마 수학을 하는 동안 납득이 가고, 프로그래밍적인 모나드의 이해와 수학적인 모나드의 이해가 일치하게 될 것이다.
여기까지 모나드는 ‘써 보는 수밖에 없다’는 말을 줄곧 해 왔는데, 김에 나도 조금 모나드 튜토리얼을 써 보려 한다. 이미 차고 넘치고, 모나드 튜토리얼만은 절대로 쓰지 않겠다고 생각했지만, 떠오른 걸 어쩌랴. 양해해 주길.
……라고는 해도 슬슬 졸려서 코드 샘플은 없고, 시안의 아웃라인만 말하겠다. 말하자면 예고편이다.
여기서는 다소 취향을 탄 서술을 하고 있고, 위에서도 말했듯 어디까지나 특수한 한 예에 지나지 않는다. 다만 다른 튜토리얼들과 함께 이 글도 이해에 조금이라도 보탬이 된다면 다행이다. 만약 어렵게 느껴진다면, 그것은 모나드가 어려워서가 아니라(아니, 어렵긴 한데, 그건 다른 프로그래밍 기법과 비슷한 정도의 어려움이다) 내 글이 서툴러서다. 게다가 아웃라인뿐이기도 하고. 그러니 ‘아, 모르겠다’ 싶으면 휙휙 넘어가도 된다.
자, 모나드란 무엇인가. 나는 명령서, 그것도 ‘합성할 수 있는 명령서’라고 말하고 싶다.
무슨 뜻인가? 애초에 모나드는 주로 함수형 프로그래밍 세계의 기법이었다. 흔히 ‘Haskell은 부작용을 허용하지 않는 순수 함수형 언어이기 때문에 모나드가 필요해진다’는 설명을 본다. 하지만 이는 정확하지 않다. 왜냐하면 OCaml이나 Scala 같은 순수하지 않은, 부작용을 허용하는 함수형 언어에서도 모나드는 편리하게 쓰이기 때문이다.
왜 이런 언어들에서 모나드가 쓰일까? 그에는 애초에 함수형 프로그래밍이란 어떤 패러다임인지 생각할 필요가 있다. 흔한 설명은 ‘함수를 일급 객체로 다룰 수 있다’, ‘대수적 데이터 타입을 가진다’, ‘재귀 함수를 기본으로 한다’ 등이다. 하지만 이들은 함수형 프로그래밍의 목표를 달성하기 위한 수단에 지나지 않는다고 나는 생각한다.
함수형 프로그래밍의 목적. 그것은 합성 가능성, 모듈러리티다. 이를 위해 함수형 언어의 기본은 ‘함수 합성’에 놓여 있다. 함수 합성야말로 함수형 프로그래밍의 본질이다. 프로그램을 여러 작은 부품 = 함수로 쪼개고, 그것들을 이어 붙여 하나의 큰 프로그램을 만든다. 개별 부품은 다른 용도에도 다양하게 재사용할 수 있다. 그것이 함수형 프로그래밍의 근저에 있는 방법론이다.
왜 함수 합성이 중요한지는 다른 글에서도 신나게 논의되어 왔으니 여기에서는 그만 두자.
자, 모나드다.
방금 모나드는 ‘합성 가능한 명령서’라고 말했다. 혹은 ‘합성 가능한 설계도’라고 해도 좋다. 이 ‘합성할 수 있다’가 중요하다. 모나드는 ‘성가신 부작용을 영리하게 합성하기 위한 틀’이다. 또 ‘부작용 그 자체’가 아니라 ‘명령서’ 혹은 ‘설계도’라는 점이 중요하다.
하지만 ‘설계도’라고 해도, 리스트 모나드라든가 Writer, Reader 같은 것은 그 자리에서 바로 실행해 버리는 것처럼 보인다. 그래서 보다 설계도다운 예로 ‘자유 모나드’라는 것을 생각해 보자. 뭔가 근엄해 보이지만, 여기서는 그런 이름의 것이 있다, 정도면 된다.
자유 모나드는 어떤 특정 모나드를 가리키는 게 아니라, 준비된 도구 상자에서 모나드를 만들어내는 방법을 가리킨다.
‘자유’라는 것은 ‘여분의 법칙으로부터 자유롭다’는 뜻. 자유 모나드는 주어진 ‘명령의 씨앗’에서 명령서를 자동으로 만들어 내는 방법이다. 대충 어떤 느낌인지 살짝 보자. 아래의 예를 보면, 모나드는 명령서·설계도라는 말이 쉽게 납득될 것이다……
……라고 쓰는 순간, 시간 종료. 졸리다. 더는 무리다. 뭐, 시안의 아웃라인이라는 걸로, 어떤 전략인지만 적어 둔다.
모나드는 합성 가능한 명령서라는 것
자유 모나드를 이용한 프로그래밍은, 명령서를 조립한 뒤 그것을 나중에 실행하는 것에 해당한다.
사실 모든 모나드는 자유 모나드를 만들고, 그것을 적절히 ‘실행’함으로써 만들 수 있다
모나드는 무섭지 않았다!
대략 이런 흐름으로, 모나드는 명령서라는 것이 상당한 설득력을 가지고 결론지어질 예정이었다. 물론, 모나드는 모나드지만 말이다. 샘플 코드 등을 준비할 수 있으면, 기회를 따로 잡아 써 보고 싶다. 또, 쓰다 말아버린 또 하나의 이유는, 자유 모나드보다 더 간편하게 모나드 명령서를 만들 수 있는 ‘Operational 모나드’라는 것도 있다고 알게 되었기 때문이고, 이것부터 검토하고 쓰는 게 낫겠다 싶었기 때문이다.
다만 내가 필부라 아마 쓰이는 건 한참 뒤가 되거나, 아니면 후미 씨가 먼저 써 주실지도 모른다. 써 주시면 좋겠다. 나는 아직 자유 모나드나 Operational 모나드를 제대로 써 본 적이 없어서……
어쨌든 모나드는 합성 가능한 명령서라는 점에 초점을 두고, 그것을 배우는 방법으로서 자유 모나드든 Operational 모나드든 직접 모나드를 만들어 보는 튜토리얼이 있어도 좋지 않을까. 라는 시안을 여기 적어 둔다.
장황하게 늘어놓았지만, 말하고 싶은 것은 아래 세 가지로 끝난다:
이상! 튜토리얼만 읽어서는 절대로 모나드를 알 수 없다. 하지만 그것은 다른 프로그래밍 개념도 마찬가지. 그러니 느긋하게, 허리를 묶고, 모나딕한 코드를 쓰자!
Have a Happy Monadic-Life!