icon안동민 개발노트

고차 함수와 커링


 고차 함수와 커링은 함수형 프로그래밍의 핵심 개념으로, 코드의 재사용성과 조합성을 높이는 데 중요한 역할을 합니다.

 타입스크립트에서 이러한 개념을 적용하면 타입 안정성과 함께 더욱 유연한 코드를 작성할 수 있습니다.

고차 함수의 정의와 구현

 고차 함수는 함수를 인자로 받거나 함수를 반환하는 함수입니다.

 타입스크립트에서의 고차 함수 예시

type Predicate<T> = (value: T) => boolean;
type Transformer<T, U> = (value: T) => U;
 
function filter<T>(array: T[], predicate: Predicate<T>): T[] {
    return array.filter(predicate);
}
 
function map<T, U>(array: T[], transformer: Transformer<T, U>): U[] {
    return array.map(transformer);
}
 
// 사용 예
const numbers = [1, 2, 3, 4, 5];
const evenNumbers = filter(numbers, n => n % 2 === 0);
const doubledNumbers = map(numbers, n => n * 2);

커링(Currying)의 개념과 구현

 커링은 여러 인자를 받는 함수를 단일 인자를 받는 함수들의 체인으로 변환하는 기법입니다.

 타입스크립트에서의 커링 구현

function curry<T1, T2, R>(fn: (a: T1, b: T2) => R): (a: T1) => (b: T2) => R {
    return (a: T1) => (b: T2) => fn(a, b);
}
 
const add = (a: number, b: number) => a + b;
const curriedAdd = curry(add);
 
console.log(curriedAdd(2)(3)); // 5

제네릭을 활용한 유연한 고차 함수

 제네릭을 사용하여 더 유연하고 재사용 가능한 고차 함수를 작성할 수 있습니다.

function compose<T, U, V>(f: (x: U) => V, g: (x: T) => U): (x: T) => V {
    return (x: T) => f(g(x));
}
 
const double = (x: number) => x * 2;
const increment = (x: number) => x + 1;
 
const doubleAndIncrement = compose(increment, double);
console.log(doubleAndIncrement(3)); // 7

부분 적용과 커링의 차이

 부분 적용(Partial Application)은 함수의 일부 인자를 미리 채우는 기법입니다. 커링과는 다르게, 남은 인자를 한 번에 제공할 수 있습니다.

function partial<T1, T2, R>(fn: (a: T1, b: T2) => R, a: T1): (b: T2) => R {
    return (b: T2) => fn(a, b);
}
 
const greet = (greeting: string, name: string) => `${greeting}, ${name}!`;
const greetHello = partial(greet, "Hello");
 
console.log(greetHello("Alice")); // "Hello, Alice!"

함수 합성(Function Composition)

 함수 합성은 여러 함수를 조합하여 새로운 함수를 만드는 기법입니다.

function compose<T>(...fns: Array<(arg: T) => T>): (arg: T) => T {
    return fns.reduce((prevFn, nextFn) => 
        (value: T) => nextFn(prevFn(value))
    );
}
 
const addOne = (x: number) => x + 1;
const double = (x: number) => x * 2;
const square = (x: number) => x * x;
 
const addOneAndDoubleAndSquare = compose(addOne, double, square);
console.log(addOneAndDoubleAndSquare(3)); // 64

타입 추론을 활용한 고차 함수와 커링

 타입스크립트의 타입 추론을 활용하여 고차 함수와 커링된 함수의 타입을 정확하게 추론할 수 있습니다.

function curry<T1, T2, R>(fn: (a: T1, b: T2) => R) {
    return (a: T1) => (b: T2) => fn(a, b);
}
 
const curriedAdd = curry((a: number, b: number) => a + b);
// curriedAdd의 타입은 (a: number) => (b: number) => number로 추론됩니다.
 
const add5 = curriedAdd(5);
// add5의 타입은 (b: number) => number로 추론됩니다.
 
console.log(add5(3)); // 8

함수형 에러 처리 패턴

 Either 모나드를 사용한 함수형 에러 처리 패턴

type Either<L, R> = Left<L> | Right<R>;
 
class Left<L> {
    constructor(private value: L) {}
    isLeft(): this is Left<L> { return true; }
    isRight(): this is Right<never> { return false; }
    fold<T>(leftFn: (left: L) => T, rightFn: (right: never) => T): T {
        return leftFn(this.value);
    }
}
 
class Right<R> {
    constructor(private value: R) {}
    isLeft(): this is Left<never> { return false; }
    isRight(): this is Right<R> { return true; }
    fold<T>(leftFn: (left: never) => T, rightFn: (right: R) => T): T {
        return rightFn(this.value);
    }
}
 
function tryCatch<T>(fn: () => T): Either<Error, T> {
    try {
        return new Right(fn());
    } catch (e) {
        return new Left(e instanceof Error ? e : new Error(String(e)));
    }
}
 
// 사용 예
const result = tryCatch(() => JSON.parse('{"name": "John"}'))
    .fold(
        error => console.error("Error:", error.message),
        data => console.log("Data:", data)
    );

Best Practices와 주의사항

  1. 타입 안정성 유지 : 제네릭과 함수 시그니처를 활용하여 타입 안정성을 확보하세요.
  2. 순수 함수 사용 : 고차 함수와 커링은 가능한 한 순수 함수로 구현하세요.
  3. 가독성 고려 : 과도한 중첩은 코드의 가독성을 해칠 수 있으므로 주의하세요.
  4. 성능 고려 : 커링과 함수 합성이 성능에 미치는 영향을 고려하세요.
  5. 부분 적용 활용 : 커링 대신 부분 적용이 더 적합한 상황인지 검토하세요.
  6. 에러 처리 : Either 모나드 등을 활용하여 일관된 에러 처리 패턴을 구축하세요.
  7. 문서화 : 복잡한 고차 함수나 커링된 함수는 사용법과 목적을 명확히 문서화하세요.
  8. 테스트 작성 : 고차 함수와 커링된 함수에 대한 단위 테스트를 작성하세요.
  9. 점진적 도입 : 팀의 역량을 고려하여 점진적으로 도입하세요.
  10. 라이브러리 활용 : 필요한 경우 lodash-fp 같은 검증된 라이브러리를 활용하세요.

 고차 함수와 커링은 타입스크립트에서 강력한 추상화와 코드 재사용 도구입니다.

 이들을 적절히 활용하면 더 선언적이고 조합 가능한 코드를 작성할 수 있습니다.

 타입스크립트와 결합하면 타입 안정성을 유지하면서도 유연한 함수형 프로그래밍 패턴을 구현할 수 있습니다.

 그러나 이러한 기법들은 올바르게 사용되지 않으면 오히려 코드를 복잡하게 만들 수 있습니다.