14장 함수형 프로그래밍 기법

자바 8 인 액션 14장을 요약한 내용 입니다.

함수는 모든 곳에 존재한다

고차원 함수란?

  • 하나 이상의 함수를 인수로 받음

  • 함수를 결과로 반환

자바 8에서는 함수를 인수로 전달할 수 있을 뿐 아니라 결과로 반환하고, 지역 변수로 할당하거나, 구조체로 삽입할 수 있으므로 자바 8의 함수도 고차원 함수라고 할 수 있다.

커링

커링은 x와 y라는 두 인수를 받는 함수 f를 한 개의 인수를 받는 g라는 함수로 대체하는 기법이다. 이때 g라는 함수 역시 하나의 인수를 받는 함수를 반환한다. 함수 g와 원래 함수 f가 최종적으로 반환하는 값은 같다. 즉 f(x,y) = (g(x))(y)가 성립한다.

이와 같은 여러 과정이 끝까지 완료되지 않은 상태를 가리켜 '함수가 부분적으로 적용되었다'라고 말한다.

영속 자료구조

함수형 메서드에서는 전역 자료구조나 인수로 전달된 구조를 갱신할 수 없다.

왜 그럴까?

자료구조를 바꾼다면 같은 메서드를 두 번 호출했을 때 결과가 달라지면서 참조 투명성에 위배되고 인수를 결과로 단순하게 매핑할 수 있는 능력이 상실되기 때문이다.

스트림과 게으른 평가

'자바 8의 스트림은 게으르다'라는 설명을 들어봤을 것이다. 자바 8의 스트림은 요청할 때만 값을 생성하는 블랙박스와 같다. 스트림에 일련의 연산을 적용하면 연산이 수행되지 않고 일단 저장된다. 스트림에 최종 연산을 적용해서 실제 계산을 해야 하는 상황에서만 실제 연산이 이루어진다.

캐싱 또는 기억화

참조 투명성이 유지되는 상황이라면 간단하게 추가 오버헤드를 피할수 있는 방법이 생긴다. 표준적인 해결책으로 기억화(memorization)라는 기법이 있다. 기억화는 메서드에 래퍼로 캐시(HashMap 같은)를 추가하는 기법이다. 래퍼가 호출되면 인수, 결과 쌍이 새키에 존재하는지 먼저 확인한다. 캐시에 값이 존재하면 캐시에 저장된 값을 즉시 반환한다. 즉, 다수의 호출자가 공유하는 자료구조를 갱신하는 기법이므로 이는 순수 함수형 해결방식은 아니지만 감싼 버전의 코드는 참조 투명성을 유지할 수 있다.

콤비네이터

함수형 프로그래밍에서는 두 함수를 인수로 받아 다른 함수를 반환하는 등 함수를 조합하는 고차원 함수를 많이 사용하게 된다. 이처럼 함수를 조합하는 기능을 콤비네이터라고 부른다.

자바 8 API에 추가된 많은 기능은 콤비네이터의 영향을 받았다. 예를 들어 CompletableFuture 클래스에는 thenCombine이라는 메서드가 추가되었다. thenCombine 메서드는 CompletableFutureBiFunction 두 인수를 받아 새로운 CompletableFuture를 생성한다.

이 개념을 활용하면 반복 과정에서 전달되는 가변 상태 함수형 모델 등 반복 기능을 좀 더 다양하게 활용할 수 있다.

정리

  • 일급 함수란 인수로 전달하거나, 결과로 반환하거나, 자료구조에 저장할 수 있는 함수다.

  • 고차원 함수란 한 개 이상의 함수를 인수로 받아서 다른 함수를 반환하는 함수다. 자바에서는 comparing, andThen, compose 등의 고차원 함수를 제공한다.

  • 커링은 함수를 모듈화하고 코드를 재사용할 수 있도록 지원하는 기법이다.

  • 영속 자료구조는 갱신될 때 기존 버전의 자신을 보존한다. 결과적으로 자신을 복사하는 과정이 따로 필요하지 않다.

  • 자바의 스트림은 스스로 정의할 수 없다.

  • 게으른 리스트는 자바 스트림보다 비싼 버전으로 간주할 수 있다. 게으른 리스트는 데이터를 요청했을 때 Supplier를 이용해서 요소를 생성한다. Supplier는 자료구조의 요소를 생성하는 역할을 수행한다.

  • 패턴 매칭은 자료형을 언랩하는 함수형 기능이다. 자바의 switch 문을 일반화할 수 있다.

  • 참조 투명성을 유지하는 상황에서는 계산 결과를 캐시할 수 있다.

  • 콤비네이터는 둘 이상의 함수나 자료구조를 조합하는 함수형 개념이다.

Last updated