Skip to content

[수정완료반영] #110 Curring #177

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 26, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 57 additions & 57 deletions 1-js/99-js-misc/03-currying-partials/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ libs:

---

# Currying
# 커링

[Currying](https://en.wikipedia.org/wiki/Currying) is an advanced technique of working with functions. It's used not only in JavaScript, but in other languages as well.
[커링 (Currying)](https://en.wikipedia.org/wiki/Currying) 은 함수와 함께 사용할 수 있는 고급기술입니다. 오직 자바스크립트에만 존재하는 것은 아니고 다른 언어에도 존재하죠.

Currying is a transformation of functions that translates a function from callable as `f(a, b, c)` into callable as `f(a)(b)(c)`.
커링은 `f(a, b, c)`처럼 단일 호출로 처리하는 함수를 `f(a)(b)(c)`와 같이 각각의 인수가 호출 가능한 프로세스로 호출된 후 병합되도록 변환하는 것입니다.

Currying doesn't call a function. It just transforms it.
커링은 함수를 호출하지 않습니다. 단지 변환할 뿐이죠.

Let's see an example first, to better understand what we're talking about, and then practical applications.
먼저 예제를 통해서 커링이 무엇인지 이해하고 그다음에 실용적인 적용법을 알아보겠습니다.

We'll create a helper function `curry(f)` that performs currying for a two-argument `f`. In other words, `curry(f)` for two-argument `f(a, b)` translates it into a function that runs as `f(a)(b)`:
`f`의 두 개의 인수를 커링하는 헬퍼 함수 `curry(f)`를 생성해 보겠습니다. 다른 말로 하면, `f(a, b)`처럼 두 개의 인수를 요구하는 함수를 `f(a)(b)` 형식으로 변환하는 `curry(f)`라는 함수를 만드는 것입니다.

```js run
*!*
function curry(f) { // curry(f) does the currying transform
function curry(f) { // 커링 변환을 하는 curry(f) 함수
return function(a) {
return function(b) {
return f(a, b);
Expand All @@ -36,84 +36,84 @@ let curriedSum = curry(sum);
alert( curriedSum(1)(2) ); // 3
```

As you can see, the implementation is straightforward: it's just two wrappers.
위의 예시에서 보듯이, 실제 구현은 그저 두 개의 래퍼를 사용한 것과 같이 간단합니다.

- The result of `curry(func)` is a wrapper `function(a)`.
- When it is called like `sum(1)`, the argument is saved in the Lexical Environment, and a new wrapper is returned `function(b)`.
- Then this wrapper is called with `2` as an argument, and it passes the call to the original `sum`.
- `curry(func)`의 반환값은 `function(a)`형태의 래퍼입니다.
- `carriedSum`이 `sum(1)`과 같은 함수를 호출했을 때처럼, 그 인수는 렉시컬 환경에 저장이 되고 새로운 래퍼 `function(b)`이 반환됩니다.
- 그리고 반환된 `function(b)`래퍼 함수가 `2`를 인수로 호출됩니다. 그리고 반환값이 원래의 `sum`으로 넘겨져서 호출됩니다.

More advanced implementations of currying, such as [_.curry](https://lodash.com/docs#curry) from lodash library, return a wrapper that allows a function to be called both normally and partially:
lodash 라이브러리의 [_.curry](https://lodash.com/docs#curry) 같이 래퍼를 반환할 때 함수가 보통 때처럼 또는 partial 적으로 호출하는 것을 허용하는 더 진보적으로 구현된 커링도 있습니다.

```js run
function sum(a, b) {
return a + b;
}

let curriedSum = _.curry(sum); // using _.curry from lodash library
let carriedSum = _.curry(sum); // lodash 라이브러리의 _.carry 사용

alert( curriedSum(1, 2) ); // 3, still callable normally
alert( curriedSum(1)(2) ); // 3, called partially
alert( carriedSum(1, 2) ); // 3, 보통 때 처럼 호출가능
alert( carriedSum(1)(2) ); // 3, partially 호출되었음
```

## Currying? What for?
## 커링은 어디에 써야할까요?

To understand the benefits we need a worthy real-life example.
커링의 이점을 이해하려면 가치가 있을 만한 실제 사례가 필요합니다.

For instance, we have the logging function `log(date, importance, message)` that formats and outputs the information. In real projects such functions have many useful features like sending logs over the network, here we'll just use `alert`:
예를 들어, 정보를 형식화하고 출력하는 로그 함수 `log (date, importance, message)`가 있다고 가정해 보겠습니다. 실제 프로젝트에서 이러한 함수는 네트워크를 통해 로그를 보내는 것과 같은 많은 유용한 기능을 제공합니다. 여기서는 `alert 창` 을 띄우는 것으로 해보겠습니다.

```js
function log(date, importance, message) {
alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`);
}
```

Let's curry it!
커링을 적용해보겠습니다!

```js
log = _.curry(log);
```

After that `log` work normally:
위와 같이 커링을 적용한 후에도 기존 함수 `log` 는 정상적으로 작동합니다.

```js
log(new Date(), "DEBUG", "some debug"); // log(a, b, c)
```

...But also works in the curried form:
...그러나 커링을 적용한 함수를 호출해도 정상적으로 동작합니다.

```js
log(new Date())("DEBUG")("some debug"); // log(a)(b)(c)
```

Now we can easily make a convenience function for current logs:
아래처럼 현재 시간으로 로그를 출력하는데 편리하도록 log 함수를 작성해서 사용할 수 있습니다.

```js
// logNow will be the partial of log with fixed first argument
// logNow 는 log 의 첫 번째 인수가 고정된 partial이 될 것입니다.
let logNow = log(new Date());

// use it
logNow("INFO", "message"); // [HH:mm] INFO message
```

Now `logNow` is `log` with fixed first argument, in other words "partially applied function" or "partial" for short.
이제 `logNow` `log`의 첫 번째 인수를 고정한 것입니다. 다른 말로 하면 "partially 적용된 함수" 또는 짧게 하면 "partial" 입니다.

We can go further and make a convenience function for current debug logs:
이제 더 나아가서 디버깅 로그를 편리하게 하는 함수를 만들어 보겠습니다.

```js
let debugNow = logNow("DEBUG");

debugNow("message"); // [HH:mm] DEBUG message
debugNow("message"); // [HH:mm] DEBUG 메세지
```

So:
1. We didn't lose anything after currying: `log` is still callable normally.
2. We can easily generate partial functions such as for today's logs.
결과적으로
1. 커링한 후에 잃은 것은 없습니다. `log`는 아직 보통 때처럼 호출할 수 있습니다.
2. 오늘의 로그 같은 partial 함수를 쉽게 작성할 수 있습니다.

## Advanced curry implementation
## 고급 커리 구현

In case you'd like to get in details, here's the "advanced" curry implementation for multi-argument functions that we could use above.
만약 좀 더 깊이 공부하고 싶다면 (필수는 아닙니다!), 예제를 통해서 다중-인수를 허용하는 "고급"커리를 구현하는 방법을 알아보겠습니다.

It's pretty short:
사실 꽤 짧습니다.

```js
function curry(func) {
Expand All @@ -131,7 +131,7 @@ function curry(func) {
}
```

Usage examples:
사용 예시입니다.

```js
function sum(a, b, c) {
Expand All @@ -140,17 +140,17 @@ function sum(a, b, c) {

let curriedSum = curry(sum);

alert( curriedSum(1, 2, 3) ); // 6, still callable normally
alert( curriedSum(1)(2,3) ); // 6, currying of 1st arg
alert( curriedSum(1)(2)(3) ); // 6, full currying
alert( curriedSum(1, 2, 3) ); // 6, 보통때 처럼 단일 callable 형식으로 호출하기
alert( curriedSum(1)(2,3) ); // 6, 첫 번째 인수를 커링하기
alert( curriedSum(1)(2)(3) ); // 6, 모두 커링하기
```

The new `curry` may look complicated, but it's actually easy to understand.
새로운 `curry`는 복잡해 보일 수도 있지만 사실 이해하기는 쉽습니다.

The result of `curry(func)` call is the wrapper `curried` that looks like this:
`curry(func)`의 반환값은 `curried`라는 아래의 래퍼와 같습니다.

```js
// func is the function to transform
// func 이 변환되어야 하는 함수입니다
function curried(...args) {
if (args.length >= func.length) { // (1)
return func.apply(this, args);
Expand All @@ -162,35 +162,35 @@ function curried(...args) {
};
```

When we run it, there are two `if` execution branches:
위의 예시를 실행시키면, 두 개의 `if` 분기점이 있습니다.

1. Call now: if passed `args` count is the same as the original function has in its definition (`func.length`) or longer, then just pass the call to it.
2. Get a partial: otherwise, `func` is not called yet. Instead, another wrapper `pass` is returned, that will re-apply `curried` providing previous arguments together with the new ones. Then on a new call, again, we'll get either a new partial (if not enough arguments) or, finally, the result.
(1)에 해당하는 경우(함수가 호출되었을때): `args` 를 카운트한 갯수가 전달된 원래 함수 func (`func.length`)와 같거나 길다면, 그대로 `func` 호출에 전달함.
(2)에 해당하는 경우(partial이 적용될때): 아직 `func`이 호출되지 않습니다. `pass`라는 래퍼가 대신 반환되고, `pass` 래퍼함수가 `curried`를 이전함수와 새로운 인수와 함께 다시 적용합니다. 그 다음 새로운 `curried` 호출에, 다시 새로운 partial (만약에 인수가 충분하지 않으면)을 반환하거나 최종적으로 `func` 결과를 반환합니다.

For instance, let's see what happens in the case of `sum(a, b, c)`. Three arguments, so `sum.length = 3`.
예를 들면, `sum(a, b, c)` 예시에서 어떻게 진행되었는지 살펴보세요. 인수가 세 개이므로 `sum.length = 3` 입니다.

For the call `curried(1)(2)(3)`:
`curried(1)(2)(3)`이 호출되는 과정은 다음과 같습니다.

1. The first call `curried(1)` remembers `1` in its Lexical Environment, and returns a wrapper `pass`.
2. The wrapper `pass` is called with `(2)`: it takes previous args (`1`), concatenates them with what it got `(2)` and calls `curried(1, 2)` with them together. As the argument count is still less than 3, `curry` returns `pass`.
3. The wrapper `pass` is called again with `(3)`, for the next call `pass(3)` takes previous args (`1`, `2`) and adds `3` to them, making the call `curried(1, 2, 3)` -- there are `3` arguments at last, they are given to the original function.
1. 첫 번째 `curried(1)` 을 호출할때 `1`을 렉시컬 환경에 기억하고 `curried(1)` 이 `pass` 래퍼를 반환합니다.
2. `pass`래퍼가 `(2)`와 함께 호출됩니다. 이전의 인수인 (`1`)을 가져서 `(2)`와 연결하고`curried (1, 2)`를 함께 호출합니다. 인수의 개수는 아직 3보다 작기때문에 `curry``pass`를 반환합니다.
3. `pass` 래퍼가 다시 `(3)`과 함께 호출됩니다. 다음 호출인 `pass(3)`가 이전의 인수들인 (`1`, `2`)를 가져오고 `3`을 추가하고 `curried(1, 2, 3)` 호출을 합니다 -- 여기에 `3`인수는 마지막으로, 원래의 함수에 전달됩니다.

If that's still not obvious, just trace the calls sequence in your mind or on the paper.
아직 확실하게 이해되지 않았다면, 호출되는 순서를 마음속이나 종이에 그려보세요.

```smart header="Fixed-length functions only"
The currying requires the function to have a fixed number of arguments.
```smart header="오직 고정된 길이의 함수들만 사용 가능합니다"
커링은 해당 함수가 고정된 개수의 인수를 가지도록 요구합니다.

A function that uses rest parameters, such as `f(...args)`, can't be curried this way.
`f(...args)`같은 나머지 매개변수를 사용하는 함수는 이러한 방법으로 커리할 수 없습니다.
```

```smart header="A little more than currying"
By definition, currying should convert `sum(a, b, c)` into `sum(a)(b)(c)`.
```smart header="커링보다는 조금 더"
커링의 정의에 따르면, 커링은 `sum(a, b, c)``sum(a)(b)(c)`으로 변환해야 합니다.

But most implementations of currying in JavaScript are advanced, as described: they also keep the function callable in the multi-argument variant.
그러나 커링의 구현은 자바스크립트에서 고급단계입니다. 이번 챕터에서 알아보았듯이 커링은 다중-인수를 단일 프로세스로 callable 한 함수를 다중 프로세스 형태로 변형할 수 있도록 하는 것입니다.
```

## Summary
## 요약

*Currying* is a transform that makes `f(a,b,c)` callable as `f(a)(b)(c)`. JavaScript implementations usually both keep the function callable normally and return the partial if arguments count is not enough.
*커링*은 `f(a,b,c)``f(a)(b)(c)` 와 같이 다중 callable 프로세스 형태로 변환하는 기술입니다. 보통 자바스크립트에서의 커링되어진 함수는 평소처럼 호출도 하고 만약에 인수들이 충분하지 않을 때에는 partial을 반환합니다.

Currying allows to easily get partials. As we've seen in the logging example: the universal function `log(date, importance, message)` after currying gives us partials when called with one argument like `log(date)` or two arguments `log(date, importance)`.
커링은 partials를 쉽게 적용할 수 있도록 합니다. 로그 예시에서 보았듯이 다목적으로 쓰였던 `log(date, importance, message)` 함수는 커링후에 `log(date)`같이 하나의 인수를 가진 형태나 `log(date, importance)`처럼 두 개의 인수를 가진 형태로 호출할 수 있었습니다.