Functional programming (often abbreviated FP) is the process of building software by composing pure functions, avoiding shared state, mutable data, and side-effects.
함수형 프로그래밍(Functional programming, FP)은 pure function으로 프로세스를 구현함으로써 공유된 state나 mutable data, side-effect를 피하는 프로그래밍 패러다임이다.
주로 함수형 프로그래밍은 Declarative(선언적)라고 하고, 그에 반대되는 개념을 Imperative(명령형)이라고 한다.
- Declarative Programming: 프로세스 로직을 control flow를 기술하지 않으면서 표현하는 프로그래밍 패러다임
ex) 그림그리는 일을 수행하고 싶을 때, 그림 그리는 일은 친구한테 다 맡겨놓고 신경쓰지 않는 것 - Imperative Programming: 프로그램의 상태를 변경하는 선언을 사용하는 프로그래밍 패러다임
ex) 내가 그림 어떻게 그리라고 하나하나 다 명령해주고 있는 것
함수형 프로그래밍은 Immutable을 강조한다. 그래서 한번 할당한 값은 변하지 않는다. 변화된 값이 필요하다면 새로 만들어서 사용한다. 그래서 객체를 선언하고 그 객체를 수정하지 않아야 한다.
(코딩을 할 때에도 값이 변하는 변수를 선언하기보다 상수를 선언해 사용하는 것이 함수형 프로그래밍에 가깝다. 실제로 디버깅이 쉬워지며 코딩을 하다보면 변수가 꼭 필요한 경우는 드물다는 것을 느낀다. 다시 말해 자바스크립트 코딩을 하며 let 키워드가 꼭 필요한 경우가 많지 않다.)
아래 예시는 배열의 모든 값에 2를 곱하는 함수들이다.
"use strict";
const arr = [1,2,3,4,5];
// 1. functional programming
function functional(arr){
const newArr = arr.map(elem=>elem*2);
return newArr;
}
// 2. not functional programming
function nonFunctional(arr){
for(let i = 0; i < arr.length; i++){
arr[i] *= 2;
}
return arr;
}
console.log(arr); // [1, 2, 3, 4, 5]
console.log(functional(arr)); // [2, 4, 6, 8, 10]
console.log(nonFunctional(arr)); // [2, 4, 6, 8, 10]
console.log(arr); // [2, 4, 6, 8, 10]
함수형 프로그래밍을 사용하면 원본 값은 변하지 않고 새로운 배열을 리턴한다. 그래서 모든 값들이 Immutable하다.
# 함수형 프로그래밍의 컨셉들
1. Higher-order functions(HOF; 고계함수) 🧙♂️
⇒ 함수를 매개변수로 가지거나, 함수를 리턴할 수 있는 함수!
예를 들면 JavaScript의 map이나 reduce, filter등의 함수를 쉽게 떠올릴 수 있다. (함수를 매개변수로 받는다.)
이미지 출처: https://www.udemy.com/course/advanced-javascript-concepts/
예제) 숫자 세 개를 더하는 경우
👉 명령형 프로그래밍으로 나타내기
간단하게 표현이 가능하다. 하지만 a,b,c중 하나의 파라미터가 누락되면 버그가 생기기 쉬운 상황이다.
"use strict";
function add(a,b,c){
return a+b+c;
}
add(1,2,3);
👉 함수형 프로그래밍(HOF를 이용해서)으로 나타내기 이렇게 HOF로 표현하는 것은 겉 보기에는 복잡해 보일 수 있다. 하지만 파라미터가 누락될 가능성이 줄어들기 때문에 안전하다.
"use strict";
function add(a){
return function(b){
return function(c){
return a + b + c;
}
}
}
const add1 = add(1);
const add2 = add1(2);
const add3 = add2(3);
console.log(add3);
2. Pure function 🧊
순수함수라는 것은 함수 파라미터에 값을 넣고 호출할 때 항상 예상된 값이 나오는 것을 말한다. 속이 그대로 투명한 순수한 함수!
👉 a
라는 값을 넣으면 항상 b
가 출력되는 것이 보장되어야 함
당연하게 보일 수 있지만, 전혀 그렇지 않다.
"use strict";
let a = 1;
function addNum(num){
return a += num;
}
console.log(addNum(2)); // 3
console.log(addNum(2)); // 5
console.log(addNum(2)); // 7
쉽게 말하자면 함수가 동작하면서 함수 외부의 값을 건드린다 ? ⇒ 순수함수가 절대 아니다!
⇒ 함수가 먼저 호출되든 나중에 호출되든 그 결과는 항상 같아야한다는 말임! (그래서 병렬프로그램같은걸 수행할 때에도 문제가 되지 않음. 그래서 컴파일러가 순서를 재배치하거나 하는 면에서 자유로워진다.)
3. Recursion
반복을 하려면 재귀함수를 호출해라!
아래 예시는 1부터 n까지 더하는 함수. 명령형 프로그래밍에서는 for
문을 사용하면 되겠지만, 함수형프로그래밍에서는 재귀함수를 사용한다.
"use strict";
function add(num){
if(num === 1) return 1;
return num + add(num-1);
}
console.log(add(5));
# 함수형 프로그래밍의 장단점
😁 장점
- 함수형 프로그래밍은 immutable하다. 그래서 항상 예측 가능하므로 버그가 적다.
- 테스트나 유닛테스트를 하기에 좋다.
- Pararllel processing 과 concurrency를 지킬 수 있다.
- 모듈화가 잘 된다. ⇒ 코드의 양도 줄어들게 됨.
- 생산성이 증가한다.
- 중첩된 함수를 지원한다.
😖 단점
- 함수형 프로그램을 작성하는 것은 어렵다. 당연히 초보자들은 이해하기 어렵다는 단점이 있다.
- 코딩의 양이 많아지면 함수형 프로그래밍을 유지하기 어려워진다.
- 재사용하는 과정이 복잡하고 계속해서 코드를 리팩토링 해줘야 한다.
함수형 프로그래밍과 객체지향 프로그래밍의 차이
둘의 가장 큰 차이점은 상태를 관리하는 관점이다. 먼저 객체지향 프로그래밍을 예시로 들면 클래스는 객체 안에서 상태를 저장한다. 그리고 해당 상태들은 ‘누가 어느 정도의 수준까지 컨트롤할 수 있는가’를 조정하기 위해서 캡슐화, scoping, visibility 등의 기능을 사용한다.
그러나 함수형 프로그램은 상태를 저장하지 않는 것에 주력한다. 함수라는 것 자체가 입력 → 출력이다보니, 그 사이에서 상태를 저장하지 않고 결과값을 바로 받아서 사용하는 것이다. 상태를 저장하지 않기 때문에 더욱 간결한 코드를 작성할 수 있다.
정리하자면, 객체지향 프로그래밍에서는 자신의 입맛에 맞게 새로운 크래스와 메소드들을 만들어서 사용한다. 반면 함수형 프로그래밍은 최소한의 자료구조(list, set, map)등을 사용해서 최적화된 동작을 만들어낸다.
Reference
- 위키백과 https://en.wikipedia.org/wiki/Functional_programming
- https://www.guru99.com/functional-programming-tutorial.html
- https://www.udemy.com/course/advanced-javascript-concepts/
- https://futurecreator.github.io/2018/10/05/why-functional-programming/