함수형 프로그래밍이란
함수형 프로그래밍은 순수함수, 수학적인 함수를 사용하는 프로그래밍입니다.
인터넷 검색을 통해서 아마 다음과 같은 정의를 볼 수 있습니다:
(순수) 함수란 같은 입력에 항상 같은 결과를 내는 부작용없는 절차입니다.
여기서 "부작용" 이란 용어의 정의를 설명하지 않았지만 (이후 공식적인 정의를 보게될것입이다) 직관적으로 파일을 열거나 데이터베이스의 쓰기같은 것을 생각해볼 수 있습니다.
지금 당장은 부작용이란 함수가 값을 반환하는 작업 이외에 하는 모든것이라고 생각하시면 됩니다.
순수함수만 사용하는 프로그램은 어떤 구조를 가질까요?
보통 함수형 프로그램은 pipeline 형태로 이루어져 있습니다.
const program = pipe(
input,
f1, // 순수 함수
f2, // 순수 함수
f3, // 순수 함수
...
)
여기서 input
은 첫 번째 함수인 f1
으로 전달되고 그 결과는 두 번째 함수인 f2
로 전달됩니다.
이어서 f2
가 반환하는 값은 세 번째 함수인 f3
로 전달되고 이후 같은 방식으로 진행됩니다.
데모
앞으로 함수형 프로그래밍이 위와 같은 구조를 만들어주는 도구를 제공하는지 보게될 것입니다.
함수형 프로그래밍이 무엇인지 이해하는 것 외에 이것의 궁극적인 목적을 이해하는 것 또한 중요합니다.
함수형 프로그래밍의 목적은 수학적인 모델 을 사용해 시스템의 복잡성을 조정하고 코드의 속성 과 리팩토링의 편의성에 중점을 두는 것입니다.
(원문) Functional programming's goal is to tame a system's complexity through the use of formal models, and to give careful attention to code's properties and refactoring ease.
함수형 프로그래밍은 프로그램 구조에 감춰진 수학을 사람들에게 가르치는 것에 도와줍니다:
- 합성 가능한 코드를 작성하는법
- 부작용을 어떻게 다루는지
- 일관적이고 범용적이며 체계적인 API 를 만드는 법
코드의 속성에 중점을 둔다는 것이 무엇일까요? 예제를 살펴보겠습니다:
예제
왜 for
반복문보다 Array
의 map
이 더 함수형이라고 할까요?
// 입력
const xs: Array<number> = [1, 2, 3]
// 수정
const double = (n: number): number => n * 2
// 결과: `xs` 의 각 요소들이 2배가 된 배열을 얻고싶다
const ys: Array<number> = []
for (let i = 0; i <= xs.length; i++) {
ys.push(double(xs[i]))
}
for
반복문은 많은 유연성을 제공합니다. 즉 다음 값들을 수정할 수 있습니다.
- 시작 위치,
let i = 0
- 반복 조건,
i < xs.length
- 반복 제어,
i++
.
이는 에러를 만들어 낼 수 있음을 의미하며 따라서 결과물에 대한 확신이 줄어듭니다.
문제. 위 for 반복문
은 올바른가요?
위 예제를 map
을 활용해 작성해봅시다.
// 입력
const xs: Array<number> = [1, 2, 3]
// 수정
const double = (n: number): number => n * 2
// 결과: `xs` 의 각 요소들이 2배가 된 배열을 얻고싶다
const ys: Array<number> = xs.map(double)
map
은 for 반복문
에 비해 유연성이 적지만 다음과 같은 확신을 제공합니다.
- 입력 배열의 모든 요소에 대해 처리될것이다.
- 결과 배열의 크기는 입력 배열의 크기와 동일할 것이다.
함수형 프로그래밍에선 구체적인 구현보다 코드의 속성에 더 집중합니다.
즉 map
연산의 제약사항이 오히려 유용하게 해줍니다.
for
반복문 보다 map
을 사용한 PR 을 리뷰할 때 얼마나 편한지 생각해보세요.