Link
Today
Total
10-17 00:15
Archives
관리 메뉴

초보개발자 긍.응.성

(모던 자바 인 액션) Chapter 18 함수형 관점으로 생각하기 본문

책 정리/모던 자바 인 액션

(모던 자바 인 액션) Chapter 18 함수형 관점으로 생각하기

긍.응.성 2020. 12. 31. 01:42
반응형

공유된 가변 데이터

변수가 예상하지 못하는 값을 갖는 이유는 시스템의 여러 메서드에서 공유된 가변 데이터 구조를 읽고 갱신하기 때문이다. 어떤 자료구조도 바꾸지 않는 시스템이 있다면 문제가 일어날 일이 없다(유지 보수하기 쉽다).

자신을 포함하는 클래스의 상태 그리고 다른 객체의 상태를 바꾸지 않으며 return 문을 통해서만 자신의 결과를 반환하는 메서드를 순수(pure) 메서드 또는 부작용 없는(side-effect free) 메서드라고 부른다. 이때 말하는 부작용은 아래와 같다.

  • 자료구조를 고치거나 필드에 값을 할당(setter)
  • 예외 발생
  • 파일에 쓰기 등의 I/O 동작 수행

선언형 프로그래밍

선언형 프로그래밍은 어떻게가 아닌 무엇을에 집중하는 프로그래밍 방식이다. 질의문 자체로 문제를 어떻게 푸는지 보여준다는 점이 명령형 프로그래밍과의 차이점이다. 선언형 프로그래밍에서는 우리가 원하는 것이 무엇이고 시스템이 어떻게 그 목표를 달성할 것인지 등의 규칙을 정한다. 문제 자체가 코드로 명확하게 드러난다는 점이 선언형 프로그래밍의 강점이다.

함수형 프로그래밍은 선언형 프로그래밍을 따르는 대표적인 방식이며, 부작용이 없는 계산을 지향한다. 선언형 프로그래밍과 부작용을 멀리한다는 두 가지 개념은 좀더 시스템을 구현하고 유지 보수하는 데 도움을 준다.

함수형 프로그래밍

함수형 프로그래밍은 말 그대로 함수를 이용하는 프로그래밍이다. 여기서 함수형 프로그래밍에서 말하는 함수는 수학적인 함수와 같다. 함수는 0개 이상의 인수를 가지며 한 개 이상의 결과를 반환하지만 부작용이 없어야 한다.

자바에서 이야기하는 수학적인 함수가 아니냐가 메서드와 함수를 구분하는 핵심이다. 함수형이라는 단어는 '수학의 함수처럼 부작용이 없는'을 의미한다. 함수는 시스템의 다른 부분에 영향을 미치지 않아야 하며 이런 특징을 참조 투명성이라 한다. 반면, 공유된 가변 데이터를 수정하여 시스템의 다른 부분에 영향을 미친다면 이는 함수형 메서드가 아니다. 또한, 인수가 같다면 수학적 함수를 반복적으로 호출했을 때 항상 같은 결과가 반환되어야 한다.

함수형 자바

자바로는 완벽한 순수 함수형 프로그래밍을 구현하기 어렵다. 하지만 시스템의 컴포넌트가 순수한 함수형인 것처럼 동작하도록 코드를 구현할 수 있다. 자바에서 구현할 것은 순수 함수형이 아닌 함수형 프로그램이다. 실제 부작용이 있지만 아무도 이를 보지못하게 함으로써 함수형을 달성할 수 있다.

함수나 메서드는 지역 변수만을 변경해야 함수형이라 할 수 있다. 그리고 함수나 메서드에서 참조하는 객체가 있다면 그 객체는 불변 객체여야 한다. 또한, 함수형이라면 함수나 메서드가 어떤 예외도 일으키지 않아야 한다. 예외가 발생할 수 있는 경우엔 Optional<T> 를 이용함으로써 연산의 수행 여부를 확인할 수 있다.

참조 투명성

참조 투명성이란 '부작용을 감춰야 한다'라는 제약으로 귀결된다. 같은 인수로 함수를 호출했을 때 항상 같은 결과를 반환한다면 참조적으로 투명한 함수라 표현한다. 참조 투명성은 다음과 같은 장점이 있다.

  • 프로그램 이해에 큰 도움을 준다.
  • 오래 걸리는 연산에 대해 기억화(memorization) 또는 캐싱(caching)을 통해 최적화할 수 있다

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

객체지향 프로그래밍과 함수형 프로그래밍은 반대되는 속성을 가진다.

객체지향 프로그래밍은 모든 것을 객체로 간주하며, 프로그램이 객체의 필드를 갱신하고, 메서드를 호출하여 관련 객체를 갱신하는 방식으로 동작한다. 반대로, 함수형 프로그래밍은 참조적 투명성을 중요시한다. 즉, 변화를 허용하지 않는 방식이다.

실제로 자바 프로그래머는 이 두 가지 형식을 혼합하여 사용한다.

재귀와 반복

순수 함수형 프로그래밍 언어에서는 while, for 같은 반복문을 포함하지 않는다. 루프 내부에서 프로그램이 공유된 가변 데이터의 상태를 변화시킬 수 있기 때문이다. 자바 8에서는 반복을 스트림으로 대체해서 변화를 피할 수 있다.

모든 반복은 재귀로 표현할 수 있다. 하지만 재귀는 반복보다 더 비싸다. 이유는 재귀적인 함수 호출로 인해 스택에 쌓이는 데이터 때문이다. 호출마다 쌓이는 스택 프레임으로 인하여 메모리 사용량이 증가하며, 계속되는 경우 StackOverflowError가 발생한다.

함수형 언어에서는 재귀호출 시 꼬리 재귀에 대하여 꼬리 호출 최적화(tail-call optimization, CTO)라는 해결책을 제공한다. CTO는 컴파일 시 하나의 스택 프레임으로 재활용하도록 하는 최적화이며, 속도의 손실 또한 없다. 안타깝게도 현재 자바에서는 CTO를 지원하지 않는다. 하지만 이후 CTO를 지원하도록 발전될 수 있는 컴파일러를 위해 재귀 코드가 있다면 꼬리 재귀적으로 작성하는 것이 좋다(스칼라와 그루비와 같은 최신 JVM언어는 CTO를 지원한다).

반응형
Comments