Skip to content

Instantly share code, notes, and snippets.

@nattybear
Last active July 17, 2022 16:03
Show Gist options
  • Save nattybear/874f0e3764aae7c81c2c120cae0cc6aa to your computer and use it in GitHub Desktop.
Save nattybear/874f0e3764aae7c81c2c120cae0cc6aa to your computer and use it in GitHub Desktop.
Functor, Natural Transformation

이 글은 책 Haskell Programming from First Principles 를 읽고 일부 내용을 정리한 것입니다.

Functor

fmap
  :: (Functor f)
  => (a -> b) -> f a -> f b

Functor는 구조는 그대로 두고 안에 들어 있는 값만 바꾼다.

f a에서 f는 구조이고 a는 값이다.

ghci> fmap (+1) [1, 2]
[2, 3]
ghci> fmap (+1) (Just 1)
Just 2

Natural Transformation

Natural Transformation은 Functor와는 반대로 안에 들어 있는 값은 그대로 두고 구조만 바꾼다.

{-# LANGUAGE RankNTypes #-}

type Nat f g =
  forall a . f a -> g a

값인 a는 그대로 두고 구조인 fg로 바꾸는 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에 대해서 어떠한 연산도 시도할 수 없다.

감사

디스코드 서버 하스켈 학교의 모든 분

대문 링크

@lionhairdino
Copy link

forall을 이런 용도로 쓸수도 있네요. 좋은 내용 감사합니다.

@lionhairdino
Copy link

혹시 윗 글을 보시고, 예시를 찾는 분들을 위해서 남겨 놓습니다.
PurescriptHalogen 라이브러리를 보다 보면, 위에서 얘기하고 있는 forall로 생기는 제약을 아주 잘 써먹습니다.
https://purescript-halogen.github.io/purescript-halogen/guide/05-Parent-Child-Components.html
컴포넌트들이 받는 인자에 forall을 걸어두면 아무런 값도 받지 않는다는 뜻으로 쓰이고 있습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment