커링의 활용 + 팁

이 내용은 함수형 프로그래밍 강의 에서 질문된 것에 대한 답변으로, 공유하면 좋을 내용이라 공개합니다.

Currying

1
func f1(_ a:Any, _ b: Any, _ c: Any) -> Any

이 함수를 커링하면

1
func f2(_ a:Any) -> (Any) -> (Any) -> Any

가 됩니다.

커링은 파라미터를 하나씩만 갖는 함수들로 쪼개는 것입니다.
당연히 f1(1,2,3) 의 결과나 f2(1)(2)(3)의 결과나 같아야 하겠죠.

커링은 함수들의 합성을 용이하게도 해주지만, 하나의 함수를 다양하게 사용할 수 있도록 해주기도 합니다.

예를 들어

1
2
3
4
func request(_ baseUrl: String,
_ method: HttpMethod,
_ path: String,
_ parameters: [String:Any]?) -> HttpRequest

이러한 함수가 있다고 할 때 이것을 커링하면 이렇게 사용할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
func request(_ baseUrl: String) -> (HttpMethod) -> (String) -> ([String:Any]?) -> HttpRequest

let oldApiRequest = request("legacy_api_server")
let newApiRequest = request("new_api_server")

let oldRequestGet = oldApiRequest(.get)
let oldRequestPost = oldApiRequest(.post)
...

//레거시 서버에 get 요청하기
let httpRequest = oldRequestGet("/list")(nil)
//신규 서버에 post 요청하기
let httpRequest2 = newRequestPost("/post")(["param":"data"])

이렇게 하면 하나의 함수를 가지고 여러 용도의 함수를 만드는 것과 같은 효과를 낼 수 있습니다.

이러한 부분을 습득하시고 난 후 커링을 고려해서 함수를 만들게 된다면 어떤 순서로 파라미터를 배치하는 것이 활용성에 좋을지를 고민하는 단계로 들어서게 될 겁니다.

그리고 잘 설계된 소스들을 보면서 함수의 파라미터 순서까지 살펴보는 관점을 가질 수 있게 될 것입니다.

Currying Tip

매번 커링된 함수를 새로 만들 필요 없이 커링시켜주는 함수를 따로 만들어 사용할 수도 있겠죠?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func currying<A,B,C,D,E>(_ f: @escaping (A, B, C, D) -> E) -> (A) -> (B) -> (C) -> (D) -> E {
return { a in
return { b in
return { c in
return { d in
return f(a, b, c, d)
}
}
}
}
}

func request(_ baseUrl: String,
_ method: HttpMethod,
_ path: String,
_ parameters: [String:Any]?) -> HttpRequest

let oldApiRequest = currying(request)("legacy_api_server")