\begin{equation}
f:(x,y) \mapsto z
\end{equation}
2개의 입력값 \(x, y\)를 받아서 \(z\)값을 출력하는 함수이다. 좀 더 간단하게 필요 없는 내용은 없애고 다음과 같은 예를 생각하자.
\begin{equation}
\text{add}: (x,y) \mapsto x+y
\end{equation}
이를 Haskell
에서 구현하면,
Prelude> let add x y = x+y
와 같다. 여기서는 interactive command line에서 함수를 정의하기 때문에 let
키워드를 사용하였는데, 일반적으로 .hs 파일과 같은 source code를 작성하는 경우에는 add x y = x+y
만으로 충분하다.
Prelude> add 3 4
7
Prelude> add 5 7
12
실행결과이다.
여기까지 쉽게 왔을지도 모른다. 그런데 여기에 약간의 반전이 있다.
Haskell
은 2개의 입력값을 받는 함수가 없다. 그렇게 만들 수도 없다. Haskell
의 함수는 오직 1개의 입력만 받을 수 있고, 1개의 출력만을 내보낸다. 뭐지? 방금 add x y
라는 함수를 만들어서 테스트도 했는데. Haskell
은 항상 simple한 것을 좋아하기 때문에 함수의 입력값과 출력값을 오직 1개만 가질 수 있게 제한해 놓았다.
그럼 방금 우리가 했던 것이 어떻게 동작할 수 있었는지 알아보자. 2개 이상의 입력값을 받는 함수는 모두 1개의 입력값을 받는 함수”들”의 조합으로 항상 변환할 수 있다.
\begin{equation}
\text{add}: (x,y) \mapsto x+y
\end{equation}
이 add
라는 함수를 변환해 보자.
\begin{equation}
f_A: x \mapsto f_B \\
f_B:y \mapsto x+y
\end{equation}
여기서 함수 \(f_A\)는 입력값 \(x\)를 받아서 함수 \(f_B\)를 반환한다. 여기서 반환값이 함수 그 자체라는 것이 중요하다. 이 함수 \(f_B\)는 어떤 함수이냐면, 입력값 \(y\)를 받아서 \(x+y\)를 출력값으로 반환하는 함수이다.
위에서 계산하였던 add 3 4
를 예로 계산해보자. 이것은 ((add 3) 4)
와 같은 순서로 계산된다. add 3
이 먼저 계산이 되는데, 이 계산은 함수 \(f_B:y \mapsto 3+y\)를 반환한다. 이 함수는 어떤 입력값 1개를 받아서 3을 더하는 함수이다. 그러면 ((add 3) 4)
는 fB 4
가 되고, 4에 3을 더한 값인 7이 반환되게 된다. 이 과정을 내부적으로 Haskell
이 처리해 준다. 입력값이 3개 혹은 그 이상이더라도 비슷한 과정이 반복되는 것이다. 이러한 과정을 커링(Currying)이라고 부른다.
왜 Currying이라고 불렀을까? Curry는 Haskell의 last name이다. 언어 Haskell
은 “Haskell B. Curry”라는 수학자의 이름을 딴 것인데, 이런 함수의 수학적 변환을 많이 연구했나보다.
정리하면,
첫 번째 입력값을 받아 함수를 반환하고, 이 반환된 함수에 두 번째 입력값을 넣으면 또 함수가 반환되고, … , 마지막 입력값을 넣으면, 최종 결과를 반환해 준다. 다시 말해 여러 개의 입력값을 받는 함수는 1개의 입력값을 받는 함수들의 조합(sequence of functions with one argument)들로 변환할 수 있고, Haskell
은 이러한 변환으로 모든 함수를 처리한다. 좀 더 수학적인 과정을 보고 싶은 분들은 wiki page를 참고.