class Functor f where fmap :: (a -> b) -> (f a -> f b) class Functor f => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b class Applicative m => Monad m where (>>=) :: m a -> (a -> m b) -> m b

(Functor, Applicative, Monad의 정의)
참고로 Monad가 Applicative를 superclass로 가진다 해서 <*>와 >>=가 orthogonal한(?) 기능을 가진다고 생각하면 안된다.
왜냐면 >>=를 가지고 <*>를 만들 수 있음.

ff <*> fx = ff >>= f -> fmap f fx


사실 Applicative, Monad 차이는 그냥 둘다 쓰다보면 충분히 느낄 수 있다.
Monad는 한마디로 만능이다. procedural한 언어에서 하는거랑 비슷함.
연산을 실행하고, 그 결과를 가지고 다른 일을 하는 이런 순차적인 작업을 표현할 수 있음.

Applicative를 가지고 할 수 있는건 n인자 함수를 lifting하는 것 뿐임.

liftA :: Applicative f => (a -> b) -> (f a -> f b) liftA2 :: Applicative f => (a -> b -> c) -> (f a -> f b -> f c) liftA3 :: Applicative f => (a -> b -> c -> d) -> (f a -> f b -> f c -> f d) ...

fmap하고 <*>만 가지고 n인자 함수 f를 인자로 받는 liftAn 함수를 만들어 낼 수 있다.

liftAn f fx1 fx2 ... fxn = f <$> fx1 <*> fx2 <*> ... <*> fxn

(<$>는 fmap의 infix operator 버전임)
<*>가 liftAn보다 훨씬 사용하기 좋음. 근데 Applicative가 뭔지는 liftAn을 봐야 이해가 될거임.
liftAn이 가진 기능은 effect를 가진 여러개의 값들을 순수함수를 사용해서 하나로 결합하는거임.

liftA2랑 >>= 타입을 들여다 보니까 좀 더 깨닫는 바가 있더라.
liftA2는 f가 달린 인자가 f a, f b임.
이건 이게 무슨 effect를 발생시킬지 이미 다 정해져 있다는거임.
잘 모르겠으면 IO Char같은거 생각해보면 됨.

반면 >>=는 a -> m b를 인자로 받음.
이건 a가 뭐가 될지에 따라서 동작이 바뀐다! 이게 제일 결정적인 차이임.
이걸 Parser combinator로 보면 context-sensitive한 문법을 표현할 수 있다고도 이해할 수 있음.

결론: Applicative는 주어진 연산들을 조합하는것만 할 수 있다. Monad는 어떤 연산을 할지를 입력을 받고서 동적으로 결정할 수 있음