이 글은 위키북스 하스켈을 읽고 정리한 것이다.
함수 map
을 이용하면 리스트 안에 들어있는 것들을 바꿀 수 있다.
GHCi> map (+1) [1,2,3]
[2,3,4]
리스트 뿐만 아니라 아래 타입 Tree
안에 들어있는 것도 바꿀 수 있다.
data Tree a
= Leaf a
| Branch (Tree a) (Tree a)
deriving (Show)
타입 Tree
안에 있는 것을 바꾸는 함수 treeMap
은 아래와 같다.
treeMap :: (a -> b) -> Tree a -> Tree b
treeMap f (Leaf x) = Leaf (f x)
treeMap f (Branch left right) =
Branch (treeMap f left) (treeMap f right)
이렇게 리스트나 Tree
같이 자기 안에 들어 있는 것을 바꿀 수 있는 것들을 타입 클래스 Functor
라고 한다.
타입 클래스 Functor
는 아래와 같이 정의되어 있다. 함수 fmap
만 정의하면 된다.
class Functor f where
fmap :: (a -> b) -> f a -> f b
함수 fmap
의 타입에서 f
를 Maybe
로 바꾸면 아래와 같다.
fmap :: (a -> b) -> Maybe a -> Maybe b
타입 Maybe
의 Functor
인스턴스 정의는 아래와 같다.
instance Functor Maybe where
fmap f Nothing = Nothing
fmap f (Just x) = Just (f x)
타입 리스트의 Functor
인스턴스 정의는 아래와 같다. 그냥 map
과 같다.
instance Functor [] where
fmap = map
위에서 정의한 타입 Tree
에 대한 Functor
인스턴스도 아래와 같이 정의할 수 있다.
instance Functor Tree where
fmap f (Leaf x) = Leaf (f x)
fmap f (Branch left right) =
Branch (fmap f left) (fmap f right)
그러니까 사실 위에서처럼 treeMap
같은 함수 이름을 따로 만들 필요는 없는 것이다.
여기서 소개한 것들은 아래와 같이 사용할 수 있다.
GHCi> fmap (2*) [1,2,3,4]
[2,4,6,8]
GHCi> fmap (2*) (Just 1)
Just 2
GHCi> fmap (fmap (2*)) [Just 1, Just 2, Just 3, Nothing]
[Just 2, Just 4, Just 6, Nothing]
GHCi> fmap (2*) (Branch (Branch (Leaf 1) (Leaf 2)) (Branch (Leaf 3) (Leaf 4)))
(Branch (Branch (Leaf 2) (Leaf 4)) (Branch (Leaf 6) (Leaf 8)))
어떤 타입의 Functor
인스턴스를 만들 때는 아래 두가지를 꼭 지켜야 한다.
fmap id = id
fmap (g . f) = fmap g . fmap f
처음 하스켈을 공부할 때 Learn You a Haskell for Great Good 같은 책을 읽으면서
도대체
Functor
가 뭐하는 거지? 뭔소리인지 전혀 모르겠다.
라면서 몇 번이고 같은 페이지를 읽었던 기억이 난다.
지금은 너무 명쾌하고 단순하고 아름다운 개념이라고 생각해서 도대체 쓸 내용이 없다고 까지 생각이 들 정도이다.
지금 뭔가 공부를 하면서 당장 이해가 안 되더라도 조급해 하지 말고 천천히 공부하면 언젠가 깨달을 날이 올 것이다.