FrontPage FindPage TitleIndex RecentChanges UserPreferences E D R S I H C
 
About All About Monads
CopyAndPasteMoniWikiGNUMakeMoniWikiBlogHelpOnActions › AboutAllAboutMonads
http://www.nomaware.com/monads/html/ 를 읽고 정리한 것으로 모나드에 대한 기초적인 이해를 정리하였습니다. 원래 noah 라는 KAIST 전산학과 BBS 에 올렸던 글입니다.

카테고리 이론에서 모나드가 어떤 의미를 갖는가는 저도 잘 모릅니다. 여기에서는 지금까지 제가 모나드에 대해 이해하고 있던 것과 바로 전에 제가 올린 글에 나온 모나드 강좌를 보고 새롭게 알게 된 것을 매우 간략히 정리하고자 합니다.


내가 이해하는 모나드

모나드란 단순화해서 말하자면 암시적인 함수 인자를 다루는 방법, 혹은 주로 드러내 보이고 싶은 것만 명시적으로 보고 나머지는 뒤에 숨겨놓는 방법입니다.

예를 들어 에러가 날 수 있는 연산을 한다고 합시다.
data Exp = Int Int | Add Exp Exp | Sub Exp Exp | Mul Exp Exp | Div Exp Exp
data Val = INT Int | ERROR String                                         

calc (Int n) = INT n
calc (Add e1 e2) =  
  case (calc e1) of
    ERROR s -> ERROR s
    INT n -> case (calc e2) of
               ERROR s -> ERROR s
               INT m -> INT(n+m) 
{- Sub, Mul 도 마찬가지 ... -}  
calc (Div e1 e2)              
  case (calc e1) of
    ERROR s -> ERROR s
    INT n -> case (calc e2) of
               ERROR s -> ERROR s
               INT 0 -> ERROR "Div by 0"
               INT m -> INT(n`div`m)    
뭔가 상당히 복잡해져 버립니다. 우리가 간단히 떠올리는 아래와 같은 코드로부터 멀어지게 됩니다.
calc (Int n) = INT n
calc (Add e1 e2) = INT (calc e1 + calc e2)
{- 나머지 생략 -}

잠깐 곁가지로 빠지자면, 프로그래머는 게을러야 한다는 격언이 있습니다. 너무 열심히 타이핑하는 것을 좋아하는 프로그래머는 동료에게 고통을 줍니다. 위의 코드들을 보고 "에러처리 하려면 당연하지"라고 생각하신다면 더 많이 게을러질 필요가 있습니다. 함수를 제대로 처리하지 못하는, 이를테면 C와 같은 언어로 프로그램을 짤 때는 함수 호출을 할때마다 그 결과값에 대해 할때마다 if ( /*result is errorcode*/ ) 를 하는 것을 너무 당연한 일로 여기게 됩니다. 표현력이 많이 모자라는 언어라 사실 그것 말고는 별다른 방법이 없기 때문일지도 모르겠습니다. 함수형 언어는 대세가 될 수밖에 없습니다. 프로그래밍 언어 관련 연구자나 현장에 있는 사람들이 그 시기를 얼마나 앞당기느냐가 관건이 될 뿐입니다.

다시 줄기로 돌아옵시다. monad 를 쓰면 에러를 커튼 뒤로 숨기고 군더더기가 거의 없는 원래 생각했던 간단한 코드와 거의 비슷하게 프로그래밍할 수 있습니다.

모나드란 다음과 같은 꼴을 가지는 두 연산이 정의되는 대상을 말합니다.
(>>=)  :: m a -> (a -> m b) -> m b
return :: a -> m a                
다시 줄기로 돌아옵시다. monad 를 쓰면 에러를 커튼 뒤로 숨기고 군더더기가 거의 없는 원래 생각했던 간단한 코드와 거의 비슷하게 프로그래밍할 수 있습니다.

그리고 덤으로, 꼭 필요한 건 아니지만 이항연산자 >>= 의 첫번째 항의 결과를 두번째 항으로 넘길 필요가 없는 경우가 종종 있기 때문에 쓰는 >> 연산자를 편의상 정의합니다. 꼴은 m a -> m b -> m b 이고 그 정의는 다음과 같습니다.
m >> k = m >>= \_ -> k
여기서 a -> b 이면 a 타입의 인자를 받아 b 를 내놓는 함수 꼴이라는 것은 알겠는데 도대체 m a 가 무엇인가 하는 분들이 있을 것입니다. 이는 parametrized type 입니다. 예를 들어 List Int, List String 과 같이 어떤 정수 리스트도 있을 수 있고 문자열 리스트도 있을 수 있고 할때 이런 꼴들을 아울러서 일반적으로 꼴 변수 a 에 대해 List a 라고 쓸 수 있고 앞의 List 마저 일반화 시켜 꼴 변수를 m 을 그 자리에 대신 쓴다면 m a 라고 표시할 수 있습니다.

m a 라는 꼴이 모나드라는 뜻은, 우리가 드러내 보이고자 하는 꼴은 a 인데 우리가 보고 싶어하는 것은 물론 드러나지 않은 것까지 다 포함하여 갖고 있는 꼴이 바로 m a 라는 것입니다.

parametrize type 이 있는 타입시스템을 한번도 사용해보거나 본적이 없는 분들은 무슨 이야기를 하는지 감이 안잡힐 수도 있는데, 단순화해서 그냥
(>>=)  :: m -> (a -> m) -> m
return :: a -> m            

(>>)   :: m -> m -> m
이렇게 일단 생각하셔도 되겠습니다.

처음에 제기했던 문제와 연결되는 예를 하나 들면 위의 내용이 무슨 말인지 와닿을 것입니다. 모나드 연산을 다음과 같이 정의합시다.

INT n   >>= f = f n
ERROR s >>= f = ERROR s

return = INT
그러면 calc 를 다음과 같이 간단하면서도 처음에 복잡하게 썼던 프로그램과 같이 에러를 처리할 수 있는 프로그램을 쓸 수 있습니다.
calc (Int n) = return n
calc (Add e1 e2) = calc e1 >>= \n -> calc e2 >>= \m -> return (m+n)
{- Sub, Mul 마찬가지 -}                                            
calc (Div e1 e2) = calc e1 >>= \n -> calc e2 >>= \m ->
                       if m=0 then ERROR "Div by 0" else return (m`div`n)
분명히 글자 개수는 줄어든 것 같은데, 읽기가 상당히 불편하다고 아직 실망하기엔 이릅니다. 다음과 같이 일반 이항연산자를 모나드에 대한 이항연산자로 들어올리는 함수를 정의합시다.
liftM2 op m1 m2 = m1 >>= \v1 -> m2 >>= \v2 -> return (v1`op`v2)
그리고 새로이 모나드에 대한 사칙연산을 정의합니다.

에러를 실제로 발생시키는 나누기 연산을 제외하고는 모두 위 함수를 사용해 다음과 같이 정의할 수 있습니다.
aDD = liftM2 (+)
sUB = liftM2 (-)
mUL = liftM2 (*)
dIV m1 m2 = m1 >>= \v1 -> m2 >>= \v2 -> 
            if v2=0 then ERROR "Div by 0" else return (v1`div`v2) 
그러면 다음과 같이 에러처리에 대한 부분은 모나드 뒤에 숨어 있고 에러처리를 하지 않는 간단한 코드에서처럼 핵심적인 내용만이 드러나는 프로그램을 쓸 수 있습니다.
calc (Int n) = return n
calc (Add e1 e2) = calc e1 `aDD` calc e2
{- Sub, Mul, Div 마찬가지 -}            
뒤에 숨기고자 하는 대상은 에러 처리 뿐만 아니고 어떤 것이든지 될 수 있습니다. 이것이 모나드를 사람들이 이해하기 어렵다고 느끼는 이유입니다. 정의는 허무하게도 연산 두 개인데, 대체 어떻게 돌아가는지 잘 보이지 않고 그렇다고 딱히 무엇에 써먹는지도 확실하지 않기 때문입니다. 아니 사실 원래 이것이 생긴 이유가 어떻게 돌아가는지 잘 보이지 않게 하려고 한 것일지도 모르겠습니다. 그러다 보니 처음 보면 뭐하자는 것인지 잘 이해가 안갑니다. 여담이지만 수학의 정의들이 대체로 복잡하기보다는 너무 간단하고 일반적이어서 감을 못잡는 것 같습니다. 카테고리 이론은 특히 더 그런 것 같습니니다.
last modified 2009-03-09 12:46:29
EditTextFindPageDeletePageLikePages