Curryingn개의 인자를 가진 함수단일 인자를 가진 n개의 함수 체인으로 만드는 것이다. f(x, y, z)f(x), g(y), h(z)로 분리하는 것이다. 일반적으로 currying은 함수를 반환하는 함수 형태로 구현한다. 따라서 higher-order function이 직/간접적으로 가능한 언어가 currying을 구현하기 편하다. Currying은 특히 Haskell 입문서에서 자주 다뤄진다.

두 예제에서, 위 함수는 두 개의 인자를 한 번에 받아 그 합을 반환하고, 아래 함수는 한 개의 인자를 받아, 남은 인자 하나가 채워지면 합을 반환하는 새로운 함수를 반환한다. 따라서 두 함수는 각각 다르게 호출하게 된다.(sum(1, 3), sum(1)(3))

만약 인자 a에 대한 연산 비용이 비싸고, 인자 b에 채울 값이 지금 당장 없다면, 인자 b에 채울 값이 생길 때까지 시간이 남으므로 위의 커링은 유용할 수 있다. 그러나 위의 경우 인자 a에 대해 연산 비용이 없으므로(별도의 로직을 적용하지 않고 단지 가지고만 있을 뿐이므로), 연산 비용의 분산 면에서는 큰 의미가 없다.

따라서 currying은 함수의 특정 인자에 대해 연산 비용이 비싼 경우, 그 함수에 인자를 나누어 전달하여 함수의 로직을 점차적으로 수행할 때에 유용하다. 다만 내 경우에는 함수 자체가 계산 비용이 비싼 경우는 있었어도, 함수의 특정 인자에 한정되어 계산 비용이 비싼 경우는 없었기에 이런 상황이 그리 많지 않았고 따라서 메모이제이션으로 충분히 처리할 수 있는 경우가 더 많았다.

이에 더해 인자 a, b 간의 양방향 의존성이 있다면 currying을 적용하기 더 어렵다. 단지 인자 a가 인자 b에 단방향 의존한다면, 인자 b를 먼저 받아 로직을 일부 처리하고 인자 a가 채워질 때 클로저로 인자 b에 접근해 나머지 로직을 처리하면 된다. 그러나 애초에 두 인자가 같이 필요한 로직 뿐이라면 currying의 중간에 인자 여러 개를 받는 함수를 끼워두거나 애초에 a와 b 인자 모두가 채워졌을 때 로직을 수행하는 등의 방식으로 해결한다. 내 경우에는, currying을 Python의 ORM 라이브러리인 SQLAlchemy에서 read db와 write db에 대한 connection을 관리하기 위한 context manager 함수를 정의하는 데에 사용한 적이 있다.

위의 경우 read_session_factorywrite_session_factory는 session 함수의 engine_name에 각각 'read'와 'write'를 전달하여 반환받은 새로운 함수다. 그러나 engine_name 인자는 연산의 양에 큰 영향을 끼치지 않으므로, 그냥 함수로 분리해 내더라도 성능 상에 큰 문제가 생기지 않는다.

프론트엔드 API에서 자주 쓰이기도 하고, Go 내부 라이브러리에도 커링된 함수가 몇 가지 있다고 하지만 사실 이런 커링 기법을 완전히 적용할만한 경우를 찾지는 못했다. 하지만 이런 방식의 함수 작성은 함수형 프로그래밍과 클로저 등에 대한 개념을 강화하는 데에 도움을 주는 것은 맞는 것 같다.

'프로그래밍 > 언어론과 비슷한' 카테고리의 다른 글

Currying  (0) 2019.02.08
Higher-order function  (0) 2019.02.06
Memoization  (0) 2018.09.11

+ Recent posts