Skip to content

Instantly share code, notes, and snippets.

@xnuk
Last active July 13, 2018 10:22
Show Gist options
  • Save xnuk/9e9d68c4d5f60bfcfab22bccbc3c5811 to your computer and use it in GitHub Desktop.
Save xnuk/9e9d68c4d5f60bfcfab22bccbc3c5811 to your computer and use it in GitHub Desktop.
((.) . (.))

왼쪽의 (.)g, 오른쪽의 (.)f라고 둡시다.

let g = (.) :: c -> d
    f = (.) :: a -> b
in g . f

g . f가 되려면 f의 리턴값을 g가 받아야겠죠. 따라서 c와 b는 같은 타입이 됩니다.

let g = (.) :: b -> d
    f = (.) :: a -> b
in g . f

그냥 중위 연산자 (.)의 타입을 보자면, 오른쪽 인자는 함수고, 왼쪽 인자는 역시 함수인데, 오른쪽 함수의 결과값이 왼쪽의 함수에 들어오는 형태죠. 그래서

(.) :: (y -> z) -> (x -> y) -> (x -> z)

가 됩니다. 같은 말로

(.) :: (y -> z) ->  (x -> y) ->  x -> z
(.) :: (y -> z) -> ((x -> y) -> (x -> z))

가 되죠. 여기에 함수 gf를 봅시다.

g :: (gy -> gz) -> ((gx -> gy) -> (gx -> gz))
g :: b          ->  d

f :: (fy -> fz) -> ((fx -> fy) -> (fx -> fz))
f :: a          -> b

type b = (fx -> fy) -> (fx -> fz) -- 실제로 타입 이름엔 첫 글자로 소문자가 올 순 없습니다.
type b = gy         -> gz

type gy = fx -> fy
type gz = fx -> fz

대입하자면

g     ::               ((fx -> fy) -> (fx -> fz)) -> ((gx -> (fx -> fy)) -> (gx -> (fx -> fz)))
f     :: (fy -> fz) -> ((fx -> fy) -> (fx -> fz))
g . f :: (fy -> fz)                               -> ((gx -> (fx -> fy)) -> (gx -> (fx -> fz)))

괄호를 좀 지워봅시다.

g . f :: (fy -> fz) -> ((gx -> (fx -> fy)) -> (gx -> (fx -> fz)))
g . f :: (fy -> fz) -> ((gx ->  fx -> fy ) -> (gx ->  fx -> fz ))
g . f :: (fy -> fz) ->  (gx ->  fx -> fy ) -> (gx ->  fx -> fz )
g . f :: (fy -> fz) ->  (gx ->  fx -> fy ) ->  gx ->  fx -> fz

답은 (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c 였죠?

g . f :: (fy -> fz) -> (gx -> fx -> fy) -> gx -> fx -> fz
g . f :: (b  -> c ) -> (a  -> a1 -> b ) -> a  -> a1 -> c

맞힌 거 같네요.

그래서 이게 대체 무슨 함수죠?

그냥 이런 함수입니다.

((.) . (.)) g f = \a b -> g (f a b)

한번 써먹어보죠.

ghci> let dot = ((.) . (.))
ghci> sum [3,4,5,6]
18
ghci> take 4 [3..]
[3,4,5,6]
ghci> let sumTake = sum `dot` take
ghci> sumTake 4 [3..]
18

직접 만들어 보기

그러니까,

dot g f a b = g (f a b)

에서 ((.).(.))를 추출해 봅시다. 그 전에 익혀 두시면 좋은 규칙 하나 알려드리죠.

      f (g x) = (f . g) x
\x -> f (g x) =  f . g

문제로 돌아와서, 맨 끝에 있는 b를 소거하자면

dot g f a b = g (f a b)
dot g f a b = (g . f a) b
dot g f a   =  g . f a

이렇게 되겠네요. 다음으로 a를 소거하고 싶습니다. 어떻게 하면 좋을까요? 여기서부터 돌아올 수 없는 길을 걷게 됩니다. g . f(g .) f로 다시 쓸 수 있죠?

dot g f a = g . f a
dot g f a = (g .) (f a)
dot g f a = ((g .) . f) a
dot g f   =  (g .) . f

짠. a를 소거했습니다. 이제 f도 소거해봅시다. 뭘 어쩔 거냐면: 중위 연산자를 앞으로 보낼 겁니다.

dot g f = (g .) . f
dot g f = (.) (g .) f
dot g   = (.) (g .)

g도 빼봅시다!

dot g = (.) (g .)
dot g = (.) ((.) g)
dot g = ((.) . (.)) g
dot   = ((.) . (.))

짠.

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