Control.Monad
Control.Monad
Control.Monad 모듈은 Functor, Monad, MonadPlus 클래스와 모나드에 대한 유용한 연산들을 함께 제공해요.
본문
13.1 펑터와 모나드 클래스 (Functor and monad classes)
class Functor f where
Functor 클래스는 매핑될 수 있는(mapped over) 타입에 사용돼요. Functor의 인스턴스는 다음 법칙을 만족해야 해요:
fmap id == id
fmap (f . g) == fmap f . fmap g
리스트, Data.Maybe.Maybe, System.IO.IO에 대한 Functor 인스턴스는 이 법칙들을 만족해요.
메서드 (Methods)
fmap :: (a -> b) -> f a -> f b
instance Functor []
instance Functor IO
instance Functor Maybe
instance Ix i => Functor (Array i)
class Monad m where
Monad 클래스는 모나드(monad) 에 대한 기본 연산을 정의해요. 모나드는 수학의 한 분야인 범주론(category theory) 에서 온 개념이에요. 하지만 Haskell 프로그래머의 관점에서는 모나드를 '행동(action)들의 추상 데이터 타입'으로 생각하는 게 가장 좋아요. Haskell의 do 표현식은 모나딕 식을 쓰기 편리한 문법을 제공해요.
최소 완전 정의(Minimal complete definition): >>= 와 return.
Monad 인스턴스는 다음 법칙들을 만족해야 해요:
return a >>= k == k a
m >>= return == m
m >>= (\x -> k x >>= h) == (m >>= k) >>= h
Monad와 Functor를 모두 갖는 인스턴스는 추가로 다음 법칙도 만족해야 해요:
fmap f xs == xs >>= return . f
Prelude에 정의된 리스트, Data.Maybe.Maybe, System.IO.IO에 대한 Monad 인스턴스는 이 법칙들을 만족해요.
메서드 (Methods)
(>>=) :: m a -> (a -> m b) -> m b
두 행동을 순차적으로 결합하고, 첫 행동이 만들어낸 값을 두 번째 행동의 인자로 넘겨요.
(>>) :: m a -> m b -> m b
두 행동을 순차적으로 결합하고, 첫 행동이 만들어낸 값은 버려요. 명령형 언어의 시퀀싱 연산자(세미콜론 같은)와 같아요.
return :: a -> m a
값을 모나딕 타입 안에 주입해요.
fail :: String -> m a
메시지와 함께 실패해요. 이 연산은 모나드의 수학적 정의의 일부는 아니지만, do 표현식 안에서 패턴 매칭이 실패하면 호출돼요.
instance Monad []
instance Monad IO
instance Monad Maybe
class Monad m => MonadPlus m where
선택(choice)과 실패(failure)도 지원하는 모나드를 위한 클래스예요.
메서드 (Methods)
mzero :: m a
mplus의 항등원이에요. 다음 방정식도 만족해야 해요:
mzero >>= f = mzero
v >> mzero = mzero
mplus :: m a -> m a -> m a
결합 법칙(associative)을 만족하는 연산이에요.
instance MonadPlus []
instance MonadPlus Maybe
13.2 함수들 (Functions)
네이밍 관례 (Naming conventions)
이 모듈의 함수들은 Haskell 컨벤션을 따라요: 이름 끝에 밑줄(_)이 붙은 함수(예: mapM_)는 결과를 버리고 ()를 반환해요.
기본 모나드 함수들 (Basic Monad functions)
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM f는 sequence . map f와 동등해요.
mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
mapM_ f는 sequence_ . map f와 동등해요.
forM :: Monad m => [a] -> (a -> m b) -> m [b]
forM은 인자 순서를 뒤집은 mapM이에요.
forM_ :: Monad m => [a] -> (a -> m b) -> m ()
forM_은 인자 순서를 뒤집은 mapM_이에요.
sequence :: Monad m => [m a] -> m [a]
수열 안의 각 행동을 왼쪽에서 오른쪽으로 평가하고 결과를 모아요.
sequence_ :: Monad m => [m a] -> m ()
수열 안의 각 행동을 왼쪽에서 오른쪽으로 평가하고 결과는 무시해요.
(=<<) :: Monad m => (a -> m b) -> m a -> m b
>>=와 같지만 인자가 뒤바뀌었어요.
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
모나드의 왼쪽에서 오른쪽으로 가는 Kleisli 합성(left-to-right Kleisli composition)이에요.
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c
모나드의 오른쪽에서 왼쪽으로 가는 Kleisli 합성이에요. 인자를 뒤집은 (>=>)과 같아요.
forever :: Monad m => m a -> m b
forever act는 행동 act를 무한히 반복해요.
void :: Functor f => f a -> f ()
void value는 평가의 결과를 버리거나 무시해요. 예를 들어 IO 행동의 반환값을 무시할 때 쓰여요.
리스트 함수들의 일반화 (Generalisations of list functions)
join :: Monad m => m (m a) -> m a
join 함수는 관례적인 모나드 조인 연산자예요. 모나딕 구조의 한 단계를 제거해서, 묶인(bound) 인자를 바깥 단계로 투영하는 데 쓰여요.
msum :: MonadPlus m => [m a] -> m a
리스트 기반 concat 함수를 일반화한 것이에요.
filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]
리스트 기반 filter 함수를 일반화한 것이에요.
mapAndUnzipM :: Monad m => (a -> m (b, c)) -> [a] -> m ([b], [c])
mapAndUnzipM 함수는 첫 번째 인자를 리스트 전체에 매핑하고, 결과를 리스트의 쌍(pair)으로 돌려줘요. 주로 복잡한 데이터 구조나 상태 변환 모나드에서 사용돼요.
zipWithM :: Monad m => (a -> b -> m c) -> [a] -> [b] -> m [c]
zipWithM 함수는 zipWith를 임의의 모나드로 일반화해요.
zipWithM_ :: Monad m => (a -> b -> m c) -> [a] -> [b] -> m ()
zipWithM_은 최종 결과를 무시하는 버전의 zipWithM이에요.
foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a
foldM 함수는 결과가 모나드 안에 캡슐화된다는 점 외에는 foldl과 유사해요. foldM은 리스트 인자를 왼쪽에서 오른쪽으로 처리한다는 점을 유의해요. (>>) 와 '접는 함수(folded function)'가 교환적(commutative)이지 않다면 이는 문제가 될 수 있어요.
foldM f a1 [x1, x2, ..., xm] ==
do a2 <- f a1 x1
a3 <- f a2 x2
...
f am xm
오른쪽에서 왼쪽으로 평가해야 한다면 입력 리스트를 뒤집어야 해요.
foldM_ :: Monad m => (a -> b -> m a) -> a -> [b] -> m ()
foldM과 같지만 결과를 버려요.
replicateM :: Monad m => Int -> m a -> m [a]
replicateM n act는 행동 act를 n번 수행하고 결과를 모아요.
replicateM_ :: Monad m => Int -> m a -> m ()
replicateM과 같지만 결과를 버려요.
모나딕 식의 조건부 실행 (Conditional execution of monadic expressions)
guard :: MonadPlus m => Bool -> m ()
guard b는 b가 True이면 return (), b가 False이면 mzero예요.
when :: Monad m => Bool -> m () -> m ()
모나딕 식의 조건부 실행이에요. 예를 들어 when debug (putStr "Debugging\n")는 부울 값 debug가 True이면 문자열 Debugging\n을 출력하고, 그렇지 않으면 아무것도 하지 않아요.
unless :: Monad m => Bool -> m () -> m ()
when의 반대예요.
모나딕 리프팅 연산자 (Monadic lifting operators)
liftM :: Monad m => (a1 -> r) -> m a1 -> m r
함수를 모나드로 승격(promote)해요.
liftM2 :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
함수를 모나드로 승격해요. 모나딕 인자를 왼쪽에서 오른쪽으로 훑어요. 예를 들어:
liftM2 (+) [0,1] [0,2] = [0,2,1,3]
liftM2 (+) (Just 1) Nothing = Nothing
liftM3 :: Monad m => (a1 -> a2 -> a3 -> r) -> m a1 -> m a2 -> m a3 -> m r
함수를 모나드로 승격해요. 모나딕 인자를 왼쪽에서 오른쪽으로 훑어요(liftM2 참고).
liftM4 :: Monad m => (a1 -> a2 -> a3 -> a4 -> r) -> m a1 -> m a2 -> m a3 -> m a4 -> m r
함수를 모나드로 승격해요. 모나딕 인자를 왼쪽에서 오른쪽으로 훑어요(liftM2 참고).
liftM5 :: Monad m => (a1 -> a2 -> a3 -> a4 -> a5 -> r) -> m a1 -> m a2 -> m a3 -> m a4 -> m a5 -> m r
함수를 모나드로 승격해요. 모나딕 인자를 왼쪽에서 오른쪽으로 훑어요(liftM2 참고).
ap :: Monad m => m (a -> b) -> m a -> m b
많은 상황에서 liftM 연산들은 함수 적용을 승격하는 ap로 대체될 수 있어요.
return f `ap` x1 `ap` ... `ap` xn
는 liftMn f x1 x2 ... xn과 동등해요.
더 알아보기 (Learn more)
Functor·Monad·MonadPlus클래스의 법칙과 개념은 Haskell의 모나드 이론을 다루는 입문 문서를 참고해요.liftM·ap등의 리프팅 연산과 Applicative와의 관계는 모던한 Haskell 자료(예:Control.Applicative)를 함께 보면 좋아요.