타입 클래스 Functor
는 구조는 건드리지 않고 구조 안의 값을 바꾼다.
class Functor a where
fmap :: (a -> b) -> f a -> f b
함수 fmap
을 구현할 때는 그런 점을 염두에 두고 구조인 f
는 건드리지 않고 구조 안의 값인 a
만 바꾸는 방향으로 구현을 하면 된다. 예를 들어 타입 Maybe
의 Functor
인스턴스를 구현할 때는 구조인 Just
는 그대로 두고 안에 값인 a
만 바꾸면 된다.
instance Functor Maybe where
fmap :: (a -> b) -> Maybe a -> Maybe b
fmap _ Nothing = Nothing
fmap f (Just a) = Just $ f a
ghci> fmap (+1) (Just 2)
Just 3
ghci> fmap (+1) Nothing
Nothing
타입 State
의 정의는 아래와 같다.
newtype State s a =
State { runState :: s -> (a, s) }
상태 s
를 넣으면 값 a
와 새로운 상태가 나오는 함수이다.
State
의 Functor
인스턴스는 아래와 같다.
instance Functor (State s) where
fmap f (State sa) =
State $ \s ->
let (a, s') = sa s
in (f a, s')
Functor
는 구조는 바꾸지 않는다고 했다. 여기서 구조는 State s
인데 s
의 타입이 바뀌진 않았지만 결과적으로 뭔가 새로운 상태인 s'
가 결과로 나온다.
내가 왜 이렇게 생각했는지 모르겠지만 처음 State
의 Monad
인스턴스를 구현할 때 아래와 같이 상태의 변화를 연계시켜주지 않고 무시해버렸다.
instance Monad (State s) where
(>>=) :: State s a
-> (a -> State s b)
-> State s b
State sa >>= f =
State $ \s ->
let (a, _) = sa s
State g = f a
in g s
그런데 이렇게 상태 s
의 변화를 연계시켜주지 않고 무시했더니 문제에서 요구하는대로 동작하지 않았다.
디스코드 하스켈 학교에 문의를 했더니 임기정 님께서 이렇게 말씀해주셨다.
상태를 변화시켜주는 게
State
의 존재 의의입니다 ㅎ
그렇구나, 듣고 보니 참 맞는 말이다 ㅎ
아래처럼 구현해야 맞겠다.
instance Monad (State s) where
State sa >>= f =
State $ \s ->
let (a, s') = sa s
State g = f a
in g s'