모던 자바 인 액션 - 18장 함수형 관점으로 생각하기

Modern Java In Action 정리 - 함수형 관점으로 생각하기

  • 왜 함수형 프로그래밍을 사용하는가 ?
  • 함수형 프로그래밍은 어떻게 정의하는가 ?
  • 선언형프로그래밍과 참조 투명성
  • 함수형 스타일의 자바 구현 가이드 라인
  • 반복과 재귀

모던 자바 인 액션 책을 보고 정리한 글입니다.

시스템 구현과 유지보수

  • 함수형 프로그래밍이 제공하는 부작용 없음과 불변성이라는 개념에 대해 알아보자

공유된 가변 데이터

  • 일반적으로 변수가 예상하지 못한 값을 갖는 이유는 우리가 유지보수하는 시스템의 여러 메서드에서 공유된 값을 읽고 갱신하기 때문이다.
  • 자신을 포함하는 클래스의 상태 그리고 다른 객체의 상태를 바꾸지 않으며 return문을 통해서만 자신의 결과를 반환하는 메서드를 순수 메서드 또는 부작용 없는 메서드라고 부른다.
  • 여기서 부작용이란 함수내에 포함되지 못한 기능을 부작용이라고 한다.
    • 자료구조를 고치거나 필드에 값을 할당
    • 예외 발생
    • 파일에 쓰기 등의 I/O 동작 수행
  • 불변 객채를 이용해서 부작용을 없애는 방법도 존재.
  • 불변객체는 인스턴스화 한 다음에는 상태를 바꿀수 없기 때문에 스레드 안정성을 보장한다.

선언형 프로그래밍

  • 일반적으로 프로그래밍 방식은 크게 두가지로 나눌수 있다. 순차적으로 작업을 처리하는 How에 집중된 방법과 질의문을 활용하는 What에 관점을 두는 방법.
  • 질의문 구현방법은 내부 반복을 사용하게 되는데 이러한 방법을 선언형 프로그래밍이라고 한다. 선언형 프로그래밍에서는 우리가 원하는 것이 무엇이고, 시스템이 어떻게 그 목표를 달성할 것인지 등의 규칙을 정한다.

왜 함수형 프로그래밍인가 ?

  • 함수형 프로그래밍은 선언형 프로그래밍을 따르는 대표적인 방식이며 이전에 설명한 것 처럼 부작용이 없는 계산을 지향한다.
  • 선언형 프로그래밍과 부작용을 멀리한다는것은 시스템을 구현하고 유지보수하는데 도움을 준다.

함수형 프로그래밍이란 ?

  • 함수형 프로그래밍의 간단한 정의는 함수를 활용하는 프로그래밍 기법이라는 것이다.
  • 함수형 프로그래밍에서 함수란 수학적인 의미의 함수와 같다. 0개 이상의 인수를 가지며 한개 이상의 결과를 반환하지만 부작용이 없어야 한다.
  • 함수형이라는 말은 수학의 함수처럼 부작용이 없는 것을 의미한다.

함수형 자바

  • 실질적으로 자바에서는 완벽한 순수 함수형 프로그래밍을 구현하기는 어렵다. 그리하여 순수 함수형이 아니라 함수형 프로그램을 구현할 것이다. 실제로는 부작용이 있지만 아무도 이를 보지 못하게 함으로 함수형을 달성할 수 있다.
  • 함수나 메서드는 지역변수만을 변경해야 함수형이라고 할 수 있다. 또한 참조하는 객체가 있다면 반드시 불변객체이어야 한다.
  • 즉 객체의 모든 필드가 final이어야 하고 모든 참조 필드는 불변 객체를 직접 참조해야 한다.
  • 함수형이라면 함수나 메서드가 어떤 예외도 일으키지 않아야 한다.
    • 예외가 발생하면 return으로 결과를 반환할 수 없게 될 수 있기 때문이다. 이러한 제약은 함수형을 수학적으로 활용하는데 큰 걸림돌이 될 것이다.
    • 수학적 함수는 주어진 인수값에 대응하는 하나의 결과를 반환한다. 실제 대부분의 수학 연산은 부분 함수로 활용된다.
    • 모든 연산은 하나의 결과를 도출하게 되는데 이때 에러가 발생하면 결과를 도출하지 못하게 된다. 예외를 사용하지 않고 값을 도출하기 위해 Optional 을 사용할 수 있다.
    • 모든 상황에 Optional을 사용하는것이 아니라 다른 컴포넌트에 영향을 미치지 않도록 구현하면 된다.
    • 마지막으로 비 함수형 동작을 감출 수 있는 상황에서만 부족용을 포함하는 라이브러리 함수를 사용해야 한다.
    • 예를 들면 컬렉션을 미리 복사한다던가 발생할 수 있는 예제를 적절하게 내부에서 처리하는 방법.

참조 투명성

  • 부작용을 감춰야 한다 라는 제약은 참조 투명성 개념으로 귀결된다.
  • 즉, 같은 인수로 함수를 호출 했을 때 항상 같은 결과를 반환 한다면 참조적으로 투명한 함수라고 표현한다.
  • 다시 말해 함수는 어떤 입력이 주어졌을때 언제, 어디서 호출하던 같은 결과를 생성해야 한다.
  • 참조 투명성은 비싸거나 오래 걸리는 연산에 대해 캐싱을 적용하여 성능 최적화 기능도 제공한다.
  • 자바에서는 참조 투명성과 관련한 작은 문제가 있다. List를 반환하는 메서드를 두 번 호출한다고 가정하자. 이때 두 번의 호출 결과로 같은 요소를 포함하지만 주솟값이 다른 리스트가 반환 될 것이다.
    결과 리스트가 가변객체라면 결론적으로 리스트를 반환하는 메서드는 참조적으로 투명하지 않은 메서드가 될것이다. 이때 결과 리스트가 불변 객체를 반환한다면 두 리스트의 참조 투명성이 보장된다.
  • 함수형 코드에서는 이런 함수를 참조적으로 투명한 것으로 간주한다.

객체지향 프로그래밍고 함수형 프로그래밍

  • Java 8은 함수형 프로그래밍을 익스트림 객체지향 프로그래밍의 일종으로 간주한다.
  • 대부분의 자바 프로그래머는 무의식적으로 함수형 프로그래밍의 일부 기능과 익스트림 객체지향 프로그래밍의 일부 기능을 사용하게 될 것이다.

재귀와 반복

  • 순수 함수형 프로그래밍 언어에서는 while과 for 같은 반복문을 포함하지 않는다.
  • 반복문을 사용하면 변화가 자연스럽게 코드에 반영될 수 있기 때문이다. 예를 들면 while loop의 조건이 변경되는 경우.
  • 함수형스타일에서 다른 누군가가 변화를 알지 못한다면 아무 관계 없다고 하였다.
  • 반복문을 사용하기 보다는 재귀 호출을 이용하는것이 공유 자원에 대한 부작용을 낮출 수 있다. 하지만 재귀 호출을 사용할 경우 memory 이슈가 발생할 가능성이 있는데 이러한 경우 Tail call optimization (꼬리 호출 최적화)기법을 사용하여 해결할 수 있다.

정리

  • 공유된 가변 자료구조를 줄이는 것은 장기적으로 프로그램을 유지보수하고 디버깅하는데 도움을 준다.
  • 함수형 프로그래밍은 부작용이 없는 메서드와 선언형 프로그래밍 방식을 지향.
  • 함수형 메서드는 입력 인수와 출력 결과만을 갖는다.
  • 같은 인수값으로 함수를 호출했을 때 항상 같은 값을 반환하면 참조 투명성을 갖는 함수이다. while 같은 루프는 재귀 호출로 대체 할 수 있다.
  • 자바에서는 고전 방식의 재귀보다는 꼬리 재귀를 사용해야 컴파일러 최적화를 기대할 수 있다.