개발 공부/JavaScript 공부

[JavaScript] 함수형 프로그래밍(1)

종범2 2020. 6. 30. 09:27

함수형 프로그래밍이란

함수형 프로그래밍이란 함수를 조합하여 소프트웨어를 개발하는 방식을 의미한다. 지금까지 주로 사용해왔던 프로그래밍 방식은 객체지향 프로그래밍이었다. 객체 지향 프로그래밍에서는 객체들을 조합하여 소프트웨어를 개발했지만 이번엔 함수를 조합하자는 생각이다. 함수형 프로그래밍의 장점은 순수 함수를 강조하여 오류를 줄이고 순수 함수를 조합하여 모듈화 수준을 높인다는 점이다.

 

순수 함수와 일급 함수

순수 함수와 일급 함수는 다음의 조건을 만족하는 함수를 의미한다. 함수형 프로그래밍에서는 순수 함수와 일급 함수의 조합을 자주 사용한다.

  • 순수 함수
    1. 동일한 input에는 항상 같은 output을 반환한다.
    2. 함수의 실행이 프로그램에 영향을 주지 않는다.
  • 일급 함수
    1. 변수 안에 함수를 할당할 수 있다.
    2. 함수의 인자로 함수를 사용할 수 있다.
    3. 함수의 반한 값으로 함수를 사용할 수 있다.

add_maker

순수 함수이자 일급 함수의 예제로 가장 많이 사용하는 함수이다.

function add_maker(a){
  return function(b){
    return a + b;
  }
}

const add5 = add_maker(5);
console.log(add5(1)); // 출력값 6
const add10 = add_maker(10);
console.log(add10(1)); // 출력값 11

add_maker는 a를 인자로 받아 함수를 반환하는 함수이다. 반환하는 함수는 b를 인자로 받아 a+b값을 반환하는 함수이다. add_maker에서 반환하는 함수는 클로져 함수가 되는데 함수 스코프 밖인 a값을 함수 내에서 사용하고 그 값을 기억하고 있기 때문이다. 예제의 add5 함수는 인자에 5를 더하여 반환하는 함수가 되고 add10 함수는 인자에 10을 더하여 반환하는 함수가 된다.

 

커링 함수

함수형 프로그래밍에서는 커링 함수를 자주 사용한다. 커링 함수는 코드의 간결함을 높이고 가독성을 높이기 위해 사용한다. 사실 커링 함수 자체만 봐서는 크게 도움이 되는지 알 수 없고 나중에 커링 함수를 사용하는 예제를 봐야 한다. 우선 여기서는 커링 함수를 설명하겠다. 커링 함수는 함수를 인자로 받아 함수를 반환한다. 이때 반환하는 함수는 인자를 a만 받는 경우는 인자를 b로 받고 func(a, b)를 반환하는 함수이고, 인자를 a, b를 받는 경우는 func(a, b)값을 반환하는 함수이다.

function curry(func) {
  return function (a, b) {
    return arguments.length === 2 ? func(a, b) : b => (func(a, b))
  };
}

curry 함수를 이용하여 함수를 생성하면 다음과 같이 두 값을 차례로 받아 더하는 함수를 반환할 수 있다.

const add = curry((a, b) => a + b);
const add3 = add(3);
console.log(add(3, 2)); // 5
console.log(add(3)(2)); // 5
console.log(add3(2)); // 5

첫 번째 예시는 두 인자를 한꺼번에 받아 더한 값을 구하는 경우며 두 번째와 세 번째 경우는 먼저 3이라는 인자를 전달하여 3을 더하는 함수를 생성하고, 그 함수에 2라는 인자를 전달하여 더한 값을 구하는 경우이다.

 

함수에 인자를 전달한 순서가 아니라 반대 순서로 인자를 계산하고 싶은 경우에는 다음의 커링 함수를 사용해야 한다.

function curryr(func) {
  return function (a, b) {
    return arguments.length === 2 ? func(a, b) : function (b) { return func(b, a) };
  }
}

curryr 함수를 이용하여 함수를 생성하면 두 값을 받아 빼는 함수를 반환할 수 있다. curry값을 사용했기 때문에 인자가 차례로 전달하면 두 번째로 전달한 인자에서 그다음 전달한 인자를 뺀 값을 반환한다.

const sub = curryr((a, b) => a - b);
const sub3 = sub(3);
console.log(sub(2, 3)); // -1
console.log(sub(3)(2)); // -1
console.log(sub3(2)); // -1

sub3 함수는 3을 빼는 함수를 의미하므로 sub3(2)를 하면 1이 아니라 -1이 나와야 한다. 만약 curry 함수로 sub3을 만들었다면 3을 빼는 함수가 아니라 3에다가 어떤 수를 빼는 함수를 의미하여 가독성을 떨어뜨린다. 위와 같이 curryr 함수로 sub3을 만들면 3을 빼는 함수를 생성할 수 있다.

 

다형성

함수형 프로그래밍에서는 다형성을 중요하게 생각한다. 함수를 연속적으로 사용하는데 형 체크와 예외처리를 하면 가독성을 떨어뜨리기 때문이다. 예를 들어 객체의 속성 값을 반환하는 keys라는 함수를 사용할 때 객체가 아닌 값을 인자로 전달하더라도 에러가 아니라 빈 배열을 반환하도록 하는 방식이다.

function isObject(obj) {
    return typeof obj == 'object' && !!obj;
}

function keys(obj){
    return isObject(obj) ? Object.keys(obj) : [];
}

const get = curryr((obj, key) => {
  return obj == null ? undefined : obj[key];
})

이런 식으로 함수형 프로그래밍에서는 함수 자체가 의도하진 형태의 인자를 받았을 때에도 정상적으로 작동할 수 있도록 프로그래밍한다. 다음 글에서는 순수 함수, 일급 함수, 커링함수, 다형성을 중시하는 함수를 조합했을 때 얼마나 가독성 높게 프로그래밍을 할 수 있는지 예제로 설명하겠다.

 

참고자료

https://marpple.github.io/partial.js/