이 글은 책 Haskell Programming from First Principles 를 읽고 일부 내용을 정리한 것입니다.
fmap
:: (Functor f)
=> (a -> b) -> f a -> f bFunctor는 구조는 그대로 두고 안에 들어 있는 값만 바꾼다.
f a에서 f는 구조이고 a는 값이다.
ghci> fmap (+1) [1, 2]
[2, 3]
ghci> fmap (+1) (Just 1)
Just 2Natural Transformation은 Functor와는 반대로 안에 들어 있는 값은 그대로 두고 구조만 바꾼다.
{-# LANGUAGE RankNTypes #-}
type Nat f g =
forall a . f a -> g a값인 a는 그대로 두고 구조인 f만 g로 바꾸는 Natural Transformation이다.
아래 코드에서 값은 그대로 두고 구조만 Maybe에서 리스트로 바꾼다.
maybeToList :: Nat Maybe []
maybeToList Nothing = []
maybeToList (Just x) = [x]RankNTypes 확장과 forall a .은 생소한데 이것들 없이 Nat를 생각해보자.
어떤 게 왜 있는지 잘 모를 때는 그게 없으면 어떤 문제가 있을지 생각해보면 된다.
아래처럼 적으면 = 오른쪽에는 a가 있는데 왼쪽에는 없어서 a가 뭔지 모르겠다고 에러가 난다.
type Nat f g =
f a -> g a
-- error:
-- Not in scope:
-- type variable `a`아래처럼 = 왼쪽에도 a를 적어주면 에러가 안 난다.
type Nat f g a =
f a -> g a그런데 이렇게 하면 아래와 같이 구조가 아니라 값이 바뀌는 것을 방지할 수 없다.
degenerateMtl :: Nat Maybe [] Int
degenerateMtl Nothing = []
degenerateMtl (Just x) = [x + 1]반면에 원래처럼 forall a .을 이용하면 구조가 아니라 값이 바뀌는 것을 방지할 수 있다. 건드리고 싶어도 못 건드린다.
degenerateMtl' :: Nat Maybe []
degenerateMtl' Nothing = []
degenerateMtl' (Just x) = [x + 1]
-- error:
-- No instance for (Num a)
-- arising from a use of `+`구조가 아닌 안에 있는 값 a에 대한 정보가 없기 때문에 a에 대해서 어떠한 연산도 시도할 수 없다.
디스코드 서버 하스켈 학교의 모든 분
forall을 이런 용도로 쓸수도 있네요. 좋은 내용 감사합니다.