명령형 프로그래밍을 기반으로 개발했던 개발자들은 개발하는 소프트웨어의 크기가 커짐에 따라, 복잡하게 엉켜있는 스파게티 코드를 유지보수하는 것이 매우 힘들다는 것을 깨닫게 되었다. 그리고 이를 해결하기 위해 함수형 프로그래밍이라는 프로그래밍 패러다임에 관심을 갖게 되었다. 함수형 프로그래밍은 거의 모든 것을 순수 함수로 나누어 문제를 해결하는 기법으로, 작은 문제를 해결하기 위한 함수를 작성하여 가독성을 높이고 유지보수를 용이하게 해준다.
유명한 책인 클린 코드(Clean Code)의 저자 Robert C.Martin은 함수형 프로그래밍을 대입문이 없는 프로그래밍이라고 정의하였다.
부수 효과가 없는 순수 함수를 1급 객체로 간주하여 파라미터나 반환값으로 사용할 수 있으며, 참조 투명성을 지킬 수 있다.
여기서 부수효과(Side Effect)란 다음과 같은 변화 또는 변화가 발생하는 작업을 의미한다.
그리고 이러한 부수 효과(Side Effect)들을 제거한 함수들을 순수 함수(Pure Function)이라고 부르며, 함수형 프로그래밍에서 사용하는 함수는 이러한 순수 함수들이다.
순수 함수(Pure Function)을 이용하면 얻을 수 있는 효과는 다음과 같다.
그리고 1급 객체란 다음과 같은 것들이 가능한 객체를 의미한다.
함수형 프로그래밍에서 함수는 1급 객체로 취급받기 때문에 위의 예제에서 본 것 처럼 함수를 파라미터로 넘기는 등의 작업이 가능한 것이다. 또한 우리가 일반적으로 알고 개발했던 함수들은 함수형 프로그래밍에서 정의하는 순수 함수들과는 다르다는 것을 인지해야 한다.
마지막으로 참조 투명성(Referential Transparency)이란 다음과 같다.
명령형 프로그래밍과 함수형 프로그래밍에서 사용하는 함수는 부수효과의 유/무에 따라 차이가 있다. 그에 따라 함수가 참조에 투명한지 안한지 나뉘어 지는데, 참조에 투명하다는 것은 말 그대로 함수를 실행하여도 어떠한 상태의 변화 없이 항상 동일한 결과를 반환하여 항상 동일하게(투명하게) 실행 결과를 참조(예측)할 수 있다는 것을 의미한다.
즉, 어떤 함수 f에 어떠한 인자 x를 넣고 f를 실행하게 되면, f는 입력된 인자에만 의존하므로 항상 f(x)라는 동일한 결과를 얻는다는 것을 의미한다. 부작용을 제거하여 프로그램의 동작을 이해하고 예측을 용이하게 하는 것은 함수형 프로그래밍으로 개발하려는 핵심 동기 중 하나이다. 그리고 이러한 부분인 병렬 처리 환경에서 개발할 때 Race Condition에 대한 비용을 줄여준다. 왜냐하면 함수형 프로그래밍에서는 값의 대입이 없이 항상 동일한 실행에 대해 동일한 결과를 반환하기 때문이다.
functional:
수학에서 범함수(functional)는 함수들의 집합을 정의역으로 갖는 함수이다. 선형범함수와는 유사하지만 약간 다른 개념이다.
monad:
함수형 프로그래밍 에서 모나드는 계산을 일련의 단계로 구조화하는 방법으로, 각 단계는 값을 생성할 뿐만 아니라 잠재적 실패, 불확정성 또는 부작용과 같은 계산에 대한 추가 정보도 생성합니다. 더 공식적으로 모나드는 두 가지 연산을 갖춘 유형 생성자 M으로, 값을 모나드 컨텍스트로 들어 올리고 모나드 계산을 연결합니다. 더 간단히 말해서, 모나드는 유형 생성자에서 구현된 인터페이스 로 생각할 수 있으며 , 이를 통해 함수는 모나드를 구현하는 다양한 유형 생성자 변형(예 : , 등) 을 추상화할 수 있습니다.
모나드 개념과 용어는 원래 범주론 에서 유래되었는데 , 범주론에서 모나드는 추가 구조가 있는 엔도펑터 로 정의됩니다. 1980년대 후반과 1990년대 초반에 시작된 연구에 따르면 모나드는 겉보기에 별개로 보이는 컴퓨터 과학 문제를 통합된 함수 모델로 가져올 수 있다는 것이 밝혀졌습니다.범주론은 또한 모나드 법칙 이라고 알려진 몇 가지 형식적 요구 사항을 제공하는데, 이는 모든 모나드가 충족해야 하며 모나드 코드를 검증하는 데 사용할 수 있습니다.
모나드는 일종의 계산에 대한 의미론을 명시적으로 만들기 때문에 편리한 언어 기능을 구현하는 데에도 사용할 수 있습니다. Haskell 과 같은 일부 언어는 일반 모나드 구조와 공통 인스턴스에 대한 사전 빌드된 정의를 핵심 라이브러리 에 제공하기도 합니다.
semigroup:
추상대수학에서 semigroup은 결합법칙을 따르는 하나의 이항 연산이 부여된 대수 구조이다.
applicative: 프로그래밍 언어 분류에서 응용 프로그래밍 언어는 인수에 적용되는 함수로 구성됩니다. 적용형 언어는 기능적이며 적용형은 종종 기능적 언어의 동의어로 사용됩니다. 그러나 연결 언어는 실용적이지는 않지만 기능적일 수 있습니다.
monadic:
단일 단위 또는 요소 또는 모나드 또는 모나드를 나타낸다.
functor:
범주론에서 함자는 두 범주 사이의 함수에 해당하는 구조로, 대상을 대상으로, 사상을 사상으로 대응시킨다. 함자는 작은 범주의 범주의 사상으로 볼 수 있다. 함자의 개념은 대수적 위상수학에서 위상 공간에 대해 기본군 등의 대수적 구조를 대응시키면서 나타났다.
disjunction:
수리 논리학에서 주어진 복수 명제에 적어도 1개 이상의 참이 있는지를 나타내는 논리 연산
어떤 단어의 리스트가 있을 때 다음과 같은 문제가 주어졌다고 하자.
이러한 요구 사항을 함수형 프로그래밍 없이 개발한다면 코드가 상당히 길어지고, 복잡해질 것이다. 하지만 함수형 프로그래밍을 적용한다면 해당 요구사항들 역시 비교적 간단하게 처리할 수 있다. 해당 문제를 해결하는 코드는 다음과 같다.
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에서는 필요가 없다. 이를 통해 상태를 바꾸는 지역 변수 자체를 없앰으로써 부수 효과를 제거하여 의도하지 않은 문제를 해결할 수 있다.
우리가 주목해야 하는 부분은 위의 wordProcess 메소드 내부에서 filter나 map, collect 등과 같은 함수의 파라미터로 함수가 전달된다는 것이다. 각각의 함수로 넘어간 파라미터 함수는 다음의 역할을 한다.
다음과 같이 사용할 함수를 변수로 선언하는 것도 가능하며 이 함수를 반환하는 것도 가능하다.
@Test
void customFunction() {
Function<String, String> function = word -> word.toUpperCase();
assertThat(function.apply("text")).isEqualTo("TEXT");
}
출처: https://mangkyu.tistory.com/111 [MangKyu's Diary:티스토리]