커링

Posted by Casval's Storage on November 19, 2020

Currying은 1967년 Christopher Strachey가 수학자이며 논리학자인 하스켈 커리(Haskell Brooks Curry)의 이름을 따서 명명.

  • 여러개의 인자를 가진 함수를 호출 할 경우, 파라미터의 수보다 적은 수의 파라미터를 인자로 받으면 누락된 파라미터를 인자로 받는 기법.

  • 여러 인자를 입력받는 함수를 인자 하나만 입력 받는 함수들의 시퀀스로 변환하는 것.

    1. 인자 하나를 입력 받고 값 대신 함수를 반환
    2. 반환된 함수는 다음 인자를 입력으로 받음.
    3. 위의 과정을 모든 입력 값을 처리하고 반환하는 값이 하나만 남을 때까지 반복.

코드

1
2
3
// 일반 function
const add2 = (x, y) => x + y;
add(1, 2) // 3
1
2
3
4
5
6
7
const addCurring = x => y => x + y;
// y => x + y 에서 x는 클로저 사용
addCurring(1); // y => x + y
addCurring(1)(2); // 3

const addOne = addCurring(1);
addOne(2) // 3

사용예

1
2
3
const addCurring = x => y => x + y;
const addOne = addCurring(1);
addOne(2) // 3
1
2
3
4
const addCurring = x => y => z => x + y + z;
const addOne = addCurring(1);
const addTwo = addOne(1);
addTwo(1) // 3

사용처

함수를 재사용하는데 매우 좋다 아래는 로그를 찍는 함수이다.

1
2
3
4
5
6
const LogLevel = { debug: 'debug', error: 'error'}
const logMessage = (logLevel, message) => console.log(`${logLevel} ${message}`)

logMessage(LogLevel.debug, 'log 1st');
logMessage(LogLevel.debug, 'log 2nd');
logMessage(LogLevel.debug, 'log 3rd');

커링을 이용하여 debug와 error로 사용가능 하다.

1
2
3
4
5
6
7
8
9
10
const LogLevel = { debug: 'debug', error: 'error'}
const logger = level => message => console.log(`${level} ${message}`)
const debug = logger(LogLevel.debug)
const error = logger(LogLevel.error)

debug('log 1st')
debug('log 2st')

error('log 1st')
error('log 2st')

반복하여 출력하는 다른 예

1
2
3
4
5
6
7
8
9
10
11
let LogLevel = { debug: 'debug', error: 'error'}
let fruits: [String] = ["Apple", "Banana", "Peach", "Grape"]
let logger = (level, message) => console.log(`${level} ${message}`)

fruits.forEach(fruit => {
  logMessage(LogLevel.debug, fruit);
}

fruits.forEach {fruit => {
   logMessage(LogLevel.error, fruit)
}

커링을 사용한 경우

1
2
3
4
5
6
7
8
let LogLevel = { debug: 'debug', error: 'error'}
let fruits = ["Apple", "Banana", "Peach", "Grape"]
let logger = level => message => console.log(`${level} ${message}`)

let debug = logger(LogLevel.debug)
let error = logger(LogLevel.error)
fruits.forEach(debug);
fruits.forEach(error);

함수를 입력 받아 currying 함수로 변환하는 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var curryIt = function(uncurried) {
  var parameters = Array.prototype.slice.call(arguments, 1);
  return function() {
    return uncurried.apply(this, parameters.concat(
      Array.prototype.slice.call(arguments, 0)
    ));
  };
};

var greeter = function(greeting, separator, emphasis, name) {
  console.log(greeting + separator + name + emphasis);
};
var greetHello = curryIt(greeter, "Hello", ", ", ".");
greetHello("Heidi"); //"Hello, Heidi."
greetHello("Eddie"); //"Hello, Eddie."

Note

  • Ramda와 같은 함수형 자바스크립트 라이브러리는 필요한 매개변수를 구분할 수 있는 보다 유연한 커링 함수들이 있다.
  • Currying을 넓게 쓰고 싶으면 라이브러리를 사용하는 것이 좋다.
  • Currying 함수에 일관된 네이밍 규칙을 적용하면 코드 가독성이 좋아진다.
    • 함수에서 파생된 다른 함수들은 작동방식이 명확해야 하며 어떤 인자가 들어오는지 알 수 있어야 한다.

인자 순서의 중요성

  • 인자의 순서는 매우 중요. 앞쪽의 인자일수록 변동 가능성이 적고, 뒤의 인자 일수록 변동 가능성이 높다.