함수형 프로그래밍(Functional Programming)의 등장
- 명령형 프로그래밍 : 프로그래밍의 상태와 상태를 변경시키는 구문의 관점에서의 연산을 설명
절차 지향 프로그래밍 : 수행되어야 할 연속적인 계산 과정을 포함(C, C++)
객체 지향 프로그래밍 : 객체들의 집합으로 프로그램의 상호작용을 표현(C++, Java, C#) - 선언형 프로그래밍 : 어떻게(HOW) 할 것인가 보다는 무엇(WHAT)을 할 것인가를 표현
함수형 프로그래밍 : 순수 함수를 조합하고 프로그램을 만드는 방식(Clojure, Haskell, Elixir)
명령형 프로그래밍을 기반으로 개발했던 개발자들은 개발하는 소프트웨어의 크기가 커짐에 따라, 복잡하게 엉켜있는 스파게티 코드를 유지보수하는 것이 매우 힘들다는 것을 깨닫게 되었습니다. 그리고 이를 해결하기 위해 함수형 프로그래밍이라는 프로그래밍 패러다임에 관심을 갖게 되었습니다. 함수형 프로그래밍은 거의 모든 것을 순수 함수로 나누어 문제를 해결하는 기법으로, 작은 문제를 해결하기 위한 함수를 작성하여 가독성을 높이고 유지보수를 용이하게 해줍니다.
함수형 프로그래밍(Functional Programming)의 특징
부수 효과가 없는 순수 함수를 1급 객체로 간주하여 파라미터나 반환값으로 사용할 수 있으며, 참조 투명성을 지킬 수 있습니다.
부수효과(Side Effect)
여기서 부수효과(Side Effect)란 다음과 같은 변화 또는 변화가 발생하는 작업을 의미합니다.
- 변수의 값이 변경됨
- 자료 구조를 제자리에서 수정함
- 객체의 필드값을 설정함
- 예외나 오류가 발생하며 실행이 중단됨
- 콘솔 또는 파일 I/O가 발생함
순수 함수(Pure Function)
그리고 이러한 부수 효과(Side Effect)들을 제거한 함수들을 순수 함수(Pure Function)이라고 부르며, 함수형 프로그래밍에서 사용하는 함수는 이러한 순수 함수들 입니다.
- Memory or I/O의 관점에서 Side Effect가 없는 함수
- 함수의 실행이 외부에 영향을 끼치지 않는 함수
순수 함수(Pure Function)의 장점
순수 함수(Pure Function)을 이용하면 얻을 수 있는 효과는 다음과 같습니다.
- 함수 자체가 독립적이며 Side-Effect가 없기 때문에 Thread에 안전성을 보장받을 수 있다.
- Thread에 안정성을 보장받아 병렬 처리를 동기화 없이 진행할 수 있다.
1급 객체(First-Class Object)
그리고 1급 객체란 다음과 같은 것들이 가능한 객체를 의미합니다.
- 변수나 데이터 구조 안에 담을 수 있다.
- 파라미터로 전달 할 수 있다.
- 반환값으로 사용할 수 있다.
- 할당에 사용된 이름과 무관하게 고유한 구별이 가능하다.
함수형 프로그래밍에서 함수는 1급 객체로 취급받기 때문에 위의 예제에서 본 것 처럼 함수를 파라미터로 넘기는 등의 작업이 가능한 것이다. 또한 우리가 일반적으로 알고 개발했던 함수들은 함수형 프로그래밍에서 정의하는 순수 함수들과는 다르다는 것을 인지해야 한다.
참조 투명성(Referential Transparency)
마지막으로 참조 투명성(Referential Transparency)이란 다음과 같습니다.
- 동일한 인자에 대해 항상 동일한 결과를 반환해야 한다.
- 참조 투명성을 통해 기존의 값은 변경되지 않고 유지된다.(Immutable Data)
함수형 프로그래밍의 예제
어떤 단어의 리스트가 있을 때 다음과 같은 문제가 주어졌다고 가정합니다.
- 단어의 크기가 2 이상인 경우를 필터링한다.
- 모든 단어를 대문자로 변환한다.
- 모든 단어를 앞글자만 잘라내어 변환한다.
- 모든 단어를 스페이스로 구분한 하나의 문자열로 합친다.
이러한 요구 사항을 함수형 프로그래밍 없이 개발한다면 코드가 상당히 길어지고, 복잡해질 것입니다. 하지만 함수형 프로그래밍을 적용한다면 해당 요구사항들 역시 비교적 간단하게 처리할 수 있습니다.
public class WordProcessTest {
private final List<String> words = Arrays.asList("TONY", "a", "hULK", "B", "america", "X", "nebula", "Korea");
@Test
void wordProcessTest() {
String result = words.stream()
.filter(w -> w.length() > 1)
.map(String::toUpperCase)
.map(w -> w.substring(0, 1))
.collect(Collectors.joining(" "));
assertThat(result).isEqualTo("T H A N K");
}
}
처음 Stream API를 접하면 오히려 복잡하게 느낄수 있으므로 위의 코드를 전혀하지 못해도 크게 문제가 없습니다. 우리가 처음 객체지향 프로그래밍을 접했을 때 처럼 함수형 프로그래밍도 새로운 프로그래밍 패러다임인 만큼 학습과 적응이 필요하기 때문입니다.
기존의 for 문을 사용하면 지역 변수가 필요했을 것이지만, 함수형 기반의 Stream API에서는 필요가 없습니다. 여기서 상태를 바꾸는 지역 변수 자체를 없앰으로써 부수 효과를 제거하여 의도하지 않은 문제를 해결할 수 있습니다.
참고
- https://mangkyu.tistory.com/111
- https://velog.io/@ragnarok_code/%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%B4%EB%9E%80
'프로그래밍 패러다임' 카테고리의 다른 글
객체 지향 프로그래밍 (0) | 2023.11.29 |
---|