문서의 임의 삭제는 제재 대상으로, 문서를 삭제하려면 삭제 토론을 진행해야 합니다. 문서 보기문서 삭제토론 Haskell (문단 편집) === [[모나드]] === [include(틀:상세 내용, 문서명=Haskell/모나드)] '''>>='''[* bind라고 부른다.] >모나드는 그냥 자기 함자 범주의 모노이드일 뿐인데, [[참 쉽죠?|대체 뭐가 어렵다는 거야?]] >- 1990, 필립 와들러 (하스켈 설계 책임자)[* ...가 한 말로 알려져 있지만, [[http://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html]]에서 농담으로 써놓은 것이 퍼진 것이고, 실제로는 범주론 교과서 MacLane, ''Categories for the Working Mathematician''에 나오는 글귀다.] 지금까지 좋은 점만 나열했는데, 그럼에도 하스켈 점유율이 바닥을 치는 이유가 있고, 그 이유는 물론 여러가지가 있을 수 있겠지만, 지금 이 항목에서 이야기하는 모나드가 커다란 부분을 차지하고 있다고 해도 과언이 아니다. 모나드는 수학의 [[범주론]](Category theory)에서 사용되는 개념을 가져와 사용하는 것이다. 사실 하스켈을 배우던 사람 중 상당수가 모나드를 수학적으로 이해하려다 멘붕 후 접는다고 봐도 될 정도로 개념적 이해까지의 난이도가 높다.[* 학부 수준 이상 넘어가면 수학과 과목 중 가장 어려워지는 게 [[대수학]]이다.] 하지만, 애시당초 수학의 모나드와는 미묘하게 다른데다 실제 사용례 위주로만 습득해서만 써도 별 문제 없으며 쉽게 쓰라고 do 같은 문법도 지원해주므로 수학적인 개념이 이해가 안 간다고 그다지 난감해 할 것은 없다. 사실 [[https://www.adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html|설명만 잘 해도]]([[https://lazyswamp.tistory.com/entry/functorsapplicativesandmonadsinpictures|번역본]]) 포인터보다 이해하기 쉬운 것이 모나드이다. 가령 흔히 쓰이는 '''리스트''', [[https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise|자바스크립트의 '''Promise''']]도 모나드다. 그중에 리스트를 예시로 들어보자. 오류가 있는 값은 빈 배열 [], 올바른 값은 숫자 하나가 든 배열로 표시하기로 했다고 해 보자. {{{-- '--' 문자는 하스켈의 주석 나누기 :: Integral a => a -> [a] -- 나누기의 타입은 정수형 a를 받아 정수형 a 리스트를 반환하는 함수 나누기 0 = [] -- 0으로 나누면 오류값을 뜻하는 [] 반환 나누기 x = [24 `div` x] -- 백틱(`)으로 감싸면 div 24 x에서 24 `div` x같이 중위 표현이 가능해진다. }}} 이런 식으로 0으로 나누기 같은 오류를 예외(exception)를 던질 필요 없이 표현할 수 있다. 이 규칙을 따라 입력받은 숫자를 2배 곱해주는 곱2 함수를 만들면 {{{곱2 :: Num a => a -> [a] 곱2 x = [x * 2] 배열1 = [2] 값1 = head 배열1 -- 2. head는 배열의 첫째 값을 반환하는 함수 배열2 = 곱2 값1 -- [4] 값2 = head 배열2 -- 4 배열3 = 곱2 값2 -- [8] }}} 연산을 할 때마다 값이 배열 안에 들어가기 때문에 사용이 번거로워진다. 이를 돕기 위한 함수를 만들어보자. {{{배열에 :: [t] -> (t -> [a]) -> [a] [] `배열에` _ = [] [x] `배열에` f = f x [1] `배열에` 곱2 -- [2] [1] `배열에` 곱2 `배열에` 곱2 -- [4] [1] `배열에` 곱2 `배열에` 곱2 `배열에` 곱2 -- [8] }}} 배열에 쌓인 값이더라도 편리하게 쓸 수 있게 되었다. 그런데 어딘가 비슷한 것 같다. {{{(>>=) :: Monad m => m a -> (a -> m b) -> m b}}} 바로 >>= 연산자의 타입이 배열에 함수와 똑 닮았다. {{{[1] >>= 곱2 -- [2] [1] >>= 곱2 >>= 곱2 -- [4] [1] >>= 곱2 >>= 곱2 >>= 곱2 -- [8] }}} 이렇게 '''값을 담는 상자'''가 모나드이고, '''상자 안의 값을 꺼내 연산을 하고 다시 상자 안에 담아주는 함수'''가 '''>>='''이다. [[https://teamdable.github.io/techblog/Moand-and-Functional-Architecture|더 자세히 설명한 링크]] 막연히 어렵다 하면 대중적인 관점에서 프로그래밍 언어의 최대 난적으로 불리우는 C언어의 포인터같은 것인가 생각이 들텐데, 짧은 지문에서 모나드의 정확한 개념적 설명은 무리고, 여기서는 대충 '무엇이 어려운지' 감을 잡기 위해 범주론에서 모나드란 게 무엇인지 개념을 훑어보도록 한다. 범주(Category)는 몇 가지 규칙을 만족시키는 대상(Object)과 그 대상 사이의 사상(Morphism)[* 함수를 추상화한 것이 사상이므로 그냥 함수라고 생각해도 된다. 정확히는 집합들을 대상으로 하는 사상이 함수이다.]으로 정의된다. 그런데 저 규칙이란 것이 결합법칙 성립, 항등사상[* 역시 항등함수(입력값을 그대로 돌려주는 함수)로 생각해도 좋다.]의 존재 2개밖에 없다. 이렇게 규칙이 아주 일반적이어서 사실 거의 모든 것들이 저 규칙에 해당된다. 프로그래밍 언어도 예외가 아니다. 순수 함수형 프로그래밍 언어에서 데이터 타입을 대상으로 놓고, 그 데이터 타입 사이의 함수(프로그램)들을 사상으로 놓으면 이것도 하나의 훌륭한 범주가 된다. 여기서, 다시 두개의 범주 사이의 구조를 생각해볼 수 있는데, 범주론에서는 몇가지 규칙을 만족시키는 저런 구조를 함자(Functor)라 한다.[* 하스켈에도 Functor 타입 클래스가 존재한다. 다만 하스켈의 Functor는 범주론에서 다루는 자기함자(Endofunctor)에 해당하는데, 이는 하스켈에서 다루는 범주가 Hask하나 뿐이기 때문에 모든 함자가 자기함자이기 때문이다.] 이 규칙 역시 아주 관대해서 함수형 언어들에서 스탠다드로 사용되는 map 같이 함수 자체를 인풋으로 받는 [[고차 함수|고차함수(higher-order function)]] 몇몇이 이에 해당된다고 볼 수 있다. 여기서, 다시 두개의 함자 사이의 변환을 생각해볼 수 있겠는데, 범주론에서는 몇가지 조건을 만족하는 저런 변환을 자연 변환(Natural transformation)이라 한다. 이 조건 역시 상당히 관대한데 unit 같은 함수가 이런 조건을 만족시킨다. 여기서 다시 저 자연 변환이 관련된 몇가지 조건을 만족하는 함자 쌍을 수반함자(Adjoint functor)라 하는데, '''모나드(Monad)'''는 바로 이 수반함자쌍의 Composition 이다. 여기까지 오면 뭔소린지는 몰라도 뭐가 어려운건지 대충 이해는 갈 것이다. 그냥 함수의 함수의 함수의... 식으로 order 가 올라가고, order 가 올라갈 때마다 조건이 붙고... 최종적으로 그래서 '''그게 대체 무엇인가 오리무중에 빠지는''' 난점이 있다. (이것은 모나드를 상당히 어렵게 설명한 것이므로 크게 걱정하지 않아도 된다. 훨씬 더 쉽게 추상화하여 설명이 가능하니 찾아보기 바람) 다행히, 함수형 언어에서 사용되는 모나드 개념은 범주론을 적극적으로 사용하기보다는 아이디어만 빌려온 수준이며, 때문에 이런 쪽 전공이 아니라면 쓸데없이 시간만 잡아먹을 수학적 이해는 그냥 포기하고 사용례로 실용적인 부분만 습득하는 것도 적극적으로 권장된다. 실용적인 관점에서 모나드의 가장 중요한 용도는 Side-Effect가 있는 Impure Function을 Pure Function인 것처럼 다루는 것이다. 대략적으로 설명하자면, Side-Effect는 보통 외부 상태(이하 State)가 있고 이것에 접근하여 생긴다. 반대로 Side-Effect가 없는 Pure Function은 모든 정보가 인수로 전달되어 외부 상태에 접근하지 않는다. 그렇다면 어떤 함수에 인수로 State를 전달하고 그에 대한 결과로 State를 리턴한다면 외부 상태에 접근을 하지 않아도 되며 pure function이 된다. 이 때 State 외에도 결과값 a도 있을 것이다. 즉, 입력이 State면 출력이 (a, State)가 되는 것이다.(이것을 State→(a, State)라고 나타낸다.) 이러한 함수는 다양하게 있을 수 있으며 그 결과값도 a 뿐만 아니라 여러가지가 있을 것이다. 그런데 하스켈은 순수 함수형 언어이기 때문에 함수를 합성하는 방식(f 와 g를 합성하면 g(f(x))가 되는 것처럼)으로 작성을 해야한다. 그런데 만약 f가 State→(a, State), g가 State→(b, State)라면 f의 출력과 g의 입력 타입이 맞지 않게 된다. 이 때 f의 결과값인 (a, State)를 a와 State로 분리해서 a는 g와 결합하여 새로운 함수 g'를 만들고 State를 이 새로운 함수 g'에 전달하는 방식을 사용하게 된다. 이렇게 결합을 해주는 것이 bind(Haskell에서 >>=연산자)라고 하고 결합되는 것을 monad라고 한다. 여기서 끝이 아니라 필요하다면 함수를 더 잇는 것도 가능하며, 이렇게 여러 개를 이으면 거대한 하나의 Pure Function이 된다. (예를 들어서 위의 경우 f와 g를 결합하면 입력이 State, 출력이 (b, State)인 함수가 된다.) 이것을 조금 다른 관점에서 보자면 어떤 외부 상태 State에 대해 f, g가 순차적으로 실행하는 순수한 함수가 생긴다는 것이다. 다르게 말하자면 '''명령형 언어'''처럼 순차적으로 함수를 호출한다는 것이다. 그러다보니 하스켈에서는 모나드를 이용하여 이러한 기능을 구현하게 된다. 즉, 하스켈은 순수 함수형 언어이면서 동시에 모나드를 이용해 정의되는 명령형 언어라는 서브언어를 내부에 갖고있는셈이다. 그렇다면 반대로, C++ 이나 파이썬같이 명령형 언어이면서 함수형 표현을 지원하는 언어와 다른 것은 무엇인가... 생각이 들법도 한데, 모나드를 이용해 하스켈 내부에 정의되는 명령형 언어 시스템은 순수성(Purity)를 훼손하지 않아 참조 투명성(Referential transparency)가 유지되며 동시에 명령형 언어 부분과 함수형 언어 부분이 엄격하게 구분된다. 일반적으로, 명령형 언어 베이스에 함수형 표현을 추가한 언어들은 말그대로 저런 '표현'정도를 지원하는 정도라, 저런 구분이 존재하지 않고 섞이는 경향이 있다. 사실 모나드는 함수형 패러다임을 수용하는 언어라면 모두 이를 구현할 수 있다. 하스켈에서도 모나드는 언어 레벨에서 구현한 구조가 아니라, 수많은 타입 클래스 구현 중 ~~평범하고(?) 조그마한(!)~~ 하나이다.[* 실제로 모나드와 그 메소드를 정의하는 코드는 코멘트를 제외하면 8줄, 100글자도 안된다. 물론 Functor, Applicative등을 계승하고 있지만, 그 또한 모나드만을 위해서 만들어진 것도 아니니 과장은 아니다. 게다가 정의도 매우 짧다.이 8줄 정도 밖에 안되는 정의를 이해하지 못해서 많은 사람들이 죄절하고 있는 것이다. 읽어보면 당연한 내용만 정의되어 있고, 거기에다가 이해를 돕기 위한 주석이 코드의 4배 이상이다.] 다만 하스켈에서는 이를 보다 적극적으로 언어의 특징으로 내세워서 적용하고 있을 뿐이다. ~~그 덕에 많은 사람들이 멘붕을 하고 있기는 하지만...~~ 여담으로 모나드를 실용적으로 구현하여 사용하는 대표적인 분야가 바로 [[JavaScript]]로 작성된 여러 프레임웍이나 라이브러리 중에서 [[AJAX|Ajax]]를 처리하는 부분이다. 특히 Promise와 ES7에 추가된 async/await 문법은 하스켈의 모나드와 do이다.[* 사실 JavaScript의 Promise는 모나드 법칙을 만족하지 않는다. 하스켈을 제외한 언어에서 모나드 법칙까지 만족하는 진짜 모나드를 사용하는 경우는 드물다.] 여기에 모나드를 적극 적용하는 이유는 다른 것이 아닌, 이것을 적용하면 노가다 양이 줄고, 사용하기 편하며, 보기도 좋기 때문.저장 버튼을 클릭하면 당신이 기여한 내용을 CC-BY-NC-SA 2.0 KR으로 배포하고,기여한 문서에 대한 하이퍼링크나 URL을 이용하여 저작자 표시를 하는 것으로 충분하다는 데 동의하는 것입니다.이 동의는 철회할 수 없습니다.캡챠저장미리보기