노트

함수 합성

Function Composition

CS#js · 연결된 개념 3개

쉽게 말하면

함수 합성은 빨래가 세탁, 헹굼, 탈수를 차례로 거치듯 작은 함수들을 이어 붙여 한 함수로 만드는 거예요. 앞 단계의 결과가 다음 단계로 넘어가니, 단계를 빼거나 바꿔 끼우기 쉬워요.

비유의 한계 세탁기는 순서가 하나지만 코드에서는 적는 방향이 두 가지예요. compose는 오른쪽 함수부터, pipe는 왼쪽 함수부터 실행해서 같은 함수를 넣어도 결과가 달라질 수 있어요.

함수 합성(Function Composition)은 한 함수의 출력을 다음 함수의 입력으로 넘기는 식으로 여러 함수를 하나로 묶는 방법이다. 마지막 함수의 출력이 합성된 함수의 결과가 된다.

compose와 pipe

  • compose(f, g)(x)는 f(g(x))다. 수학의 합성처럼 오른쪽 함수부터 실행한다
  • pipe(f, g)(x)는 g(f(x))다. 적은 순서대로 실행해서 읽는 순서와 실행 순서가 같다

reduce는 배열을 왼쪽부터, reduceRight는 오른쪽부터 접는다. 둘 다 입력값 x를 누적값으로 시작해, 함수마다 결과를 다음 함수에 넘긴다.

const compose = (...fns) => (x) => fns.reduceRight((acc, fn) => fn(acc), x)
const pipe = (...fns) => (x) => fns.reduce((acc, fn) => fn(acc), x)
 
const inc = (n) => n + 1
const double = (n) => n * 2
 
compose(double, inc)(3)   // 8: inc 먼저(4) → double(8)
pipe(double, inc)(3)      // 7: double 먼저(6) → inc(7)

쓸 때 알아 둘 점

  • 이어 붙이려면 각 함수가 인수 하나를 받아 값 하나를 돌려주는 편이 좋다. 인수가 여럿인 함수는 커링이나 부분 적용으로 인수 하나짜리로 만든 뒤 끼운다
  • 함수가 Promise를 돌려준다면 시작값을 Promise.resolve(x)로 두고 fn(acc) 대신 acc.then(fn)으로 이어, 앞 작업이 끝난 뒤 다음 작업을 실행하게 할 수 있다
  • 배열 메서드를 사슬로 이어 반복문을 대신하는 것은 컬렉션 파이프라인이다. 객체 설계에서 말하는 상속보다 합성의 합성은 객체를 조합하는 이야기로, 함수 합성과는 다르다

출처: MDN - Array.prototype.reduceRight: Defining composable functions · MDN - Array.prototype.reduce: Function sequential piping · Running promises in sequence

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 함수 종속과 후보키

    함수 종속(functional dependency)은 "A 값이 정해지면 B 값이 하나로 정해진다"는 관계로, A → B로 쓴다. 학번을 알면 이름을 안다면 학번 → 이름이다. 정규화의 각 단계는 특정한 종속을 없애는 작업이다.

  • 매개변수 객체 만들기

    여러 함수에 늘 함께 넘겨지는 값 묶음(데이터 뭉치, Data Clumps)을 하나의 객체로 묶어 넘기는 리팩터링.

  • 재귀

    함수가 자기 자신을 다시 호출해 문제를 푸는 방식. 같은 함수를 점점 작은 입력으로 부르다가 더 나눌 필요가 없는 지점(기저 조건, Base Case)에서 멈춘다.

  • 함수 추출하기

    코드 조각에 목적을 드러내는 이름을 붙여 독립된 함수로 빼는 리팩터링. "어떻게"와 "무엇을"을 분리한다. 반대 방향인 함수 인라인하기(Inline Function)는 본문이 이름만큼 명확한 함수를 호출부로 다시 녹인다.

  • 함수 오버로드와 시그니처

    함수 오버로드(Function Overloads)는 구현 하나 위에 호출 방식별 시그니처를 여러 개 선언해, 인자 조합에 따라 정확한 반환 타입이 추론되게 하는 TypeScript 기능이다.

보기 옵션