위키백과에 따르면 함수형 프로그래밍은 자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임의 하나이다.
선언형은What,명령형은How에 집중하는 것이다.
선언형은 원하는 결과를 표현하기 위해 코드를 작성한다. 선언형 코드의 좋은 예시는 CSS!!
body {
font-siez:1rem;
color:skyblue;
}
직관적이다! what에 대해서만 작성할 뿐, how에 대해서는 작성하지 않는다.
선언형 코드는 원하는 결과값을 선언하는 것이다.
명령형은 원하는 결과를 얻기 위해 필요한 지침에 따라 코드를 작성한다.
function spaceToDelimiter(text, delimiter = '❤') {
let result = '';
for (let i = 0; i < text.length; ++i) {
if (text[i] === ' ') {
result += delimiter;
} else {
result += text[i];
}
}
return result
}
명령형 코드는 결과값에 어떻게 도달하는 것인가에 대한 작성이다.
위의 명령형 코드를 선언형 코드로 바꾸면 아래와 같다.
function spaceToDelimiter(text, delimiter = '❤') {
return text.replaceAll(' ', delimiter);
}
String.prototype.replaceAll()를 선언하여 사용했는데, 내부 구현은 어떻게 되어있는지 정확히 모른다. 어떻게 사용하는지만 익혀서 선언형 코드로 바로 적용할 수 있다.
순수한 함수란, 부작용(side-effect)이 없는 함수, 즉, 함수의 실행이 외부에 영향을 끼치지 않는 함수를 뜻한다. 따라서 순수한 함수는 스레드에 안전하고, 병렬적인 계산이 가능하다.
다음과 같은 코드에서 f는 순수한 함수라고 하자.
y = f(x) * f(x);
이 때, f가 두 번 중복되는 것을 다음과 같이 최적화할 수 있다.
z = f(x);
y = z * z;
이렇게 하면, f(x)를 계산하는 과정이 두 번에서 한 번으로 줄어들지만 두 코드의 결과는 항상 같게 된다.
익명 함수란, 이름이 없는 함수를 뜻한다. 전통적인 명령형 언어에서는 모든 함수에 이름이 부여되어야만 한다.
예컨대 인수를 제곱하는 함수를 C 언어로 작성한다면 다음과 같이 작성할 수 있을 것이다.
int square(int x) { return x * x; }
C++11에서는 익명 함수를 지원해줘서 위의 코드를 익명함수로 바꾸면 아래와 같다.
[](int x) -> int { return x * x; }
고차 함수(고계 함수)란, 함수를 다루는 함수를 뜻한다. 사실 함수형 언어에서는 함수도 '값(value)'으로 취급한다. 그러므로 정수 1이나 인수를 제곱하는 함수나 동등한 입장에서 다룰 수 있다. 정수를 함수의 인수로 전달할 수 있듯이 어떤 함수도 다른 함수의 인수로 전달할 수 있다. 마찬가지로 함수의 결과 값으로 정수를 반환할 수 있듯이 함수를 반환할 수도 있다.
예를 들어서 1에서 10까지 숫자로 이루어진 리스트의 각 원소를 제곱하고 싶다고 하자. 명령형 언어에서는 반복문을 이용하여 리스트를 훑어 가며 각 원소를 제곱하겠지만, 함수형 언어에서는 리스트를 다루는 고계 함수로 이를 처리할 수 있다. 다음은 하스켈을 이용하여 이를 수행한 예를 보여준다.
map (x -> x * x) [1..10]
여기서 [1..10]은 1에서 10까지 숫자로 이루어진 리스트다. 고계 함수 map은 첫 번째 인수로 주어진 함수(여기서는 제곱을 수행하는 익명함수)를 두 번째 인수로 주어진 리스트의 각 원소에 적용한 결과 리스트를 반환한다. 위 코드를 수행하면 다음과 같은 결과를 얻을 수 있다.
[1,4,9,16,25,36,49,64,81,100]