함수형 프로그래밍과 JavaScript ES6+ 강의를 듣고 정리한 내용입니다.
✅ 합성 관점에서의 Promise
결론부터 이야기하자면 Promise는 비동기 상황에서 함수 합성을 안전하게 하기 위한 도구이자 모나드이다.
함수 합성이란 f · g을 말하며 g 함수를 먼저 적용한 결괏값에 f 함수를 적용하는 것이다.
x라는 값이 있다고 할 때 f · g는 f(g(x))와 같다.
연속적으로 함수가 실행되고 함수 합성을 할 때 상황에 따라 안전하게 합성할 수 있게 하기 위해 모나드라는 개념이 필요하다.
그리고 그 구현체 중 비동기 상황을 안전하게 합성하기 위해 Promise를 사용한다.
즉, Promise는 모나드의 일종이다.
✅ Monad
모나드(Monad)는 값을 담는 컨테이너의 일종이며 Functor를 기반으로 구현되어 있다.
flatMap() 메소드를 제공하며 Monad Laws를 만족시키는 구현체이다.
Functor는 인터페이스이며 함수를 인자로 받는 map 메소드만을 가지고 있다.
Functor 자체는 T라는 타입을 가지고 있다.
map이 인자로 받는 함수는 T라는 타입을 가지고 있는데 이것의 반환값은 R 타입이다.
결국 Functor의 반환값은 R 타입을 가진다.
이를 통해 알 수 있는 것은 map의 진정한 의미가 컬렉션의 요소를 순회하는 것이 아니라 T 타입의 Functor를 R 타입의 Functor로 바꾸는 것에 있다는 것이다.
Functor를 이용하면 일반적으로 모델링할 수 없는 상황을 모델링할 수 있다.
값이 없는 케이스이거나 값이 미래에 준비될 것으로 예상되는 케이스가 그 예이다.
이는 곧 비동기적인 상황도 여기 속한다고 할 수 있다.
결론적으로 Functor를 사용하면 안전한 상황에서 함수를 합성할 수 있다.
모나드는 Functor를 보완하여 만든 것으로 Functor에 flatMap이 추가된 형태이다.
즉, 모나드를 사용하면 안전한 상황에서 함수를 합성할 수 있다.
✏️ 예제
const g = a => a + 1;
const f = a => a * a;
위의 두 함수가 있다고 가정하고 우리는 f 함수와 g 함수를 합성해 f(g(x))의 결괏값을 내고자 한다.
console.log(f(g(1)));
console.log(f(g()));
// output
// 4
// NaN
x의 자리에 바로 우리가 계산하고자 하는 값을 넣고 콘솔 로그를 찍어보면 값이 있는 경우에는 제대로 원하는 값이 출력됨을 알 수 있다.
하지만 값이 없는 경우에는 NaN이라는 값을 얻는다.
우리가 원하는 것은 이런 상황에서 에러가 나오거나 함수 실행을 하지 않는 것이지 NaN이라는 값이 아니다.
[1].map(g).map(f).forEach(r => console.log(r));
// output
// 4
f(g(1))의 결괏값을 콘솔 로그로 출력하는 것을 모나드를 사용해 표현하면 위와 같다.
[]라는 박스를 통해 실제 있는 값이 숨겨져 있고 map을 통해 함수 합성을 하는데 숨겨져 있던 값이 순차적으로 전달된다.
1이라는 값을 담기 위해 배열이라는 자료 구조를 이용했을 뿐 실제 원하는 것은 배열이 아니기에 forEach로 배열 안의 값을 꺼내 출력해준다.
console.log(f(g()));
[].map(g).map(f).forEach(r => console.log(r));
// output
// NaN
위와 같은 서술 방식의 차이는 값이 없는 경우 더 극명하게 드러난다.
f(g())에서는 NaN이 출력된 것과 반대로 [] 안에 값을 넣지 않고 함수 합성의 인자로 전달한 경우에는 아무것도 출력되지 않는다.
함수 실행조차 되지 않은 것이다.
박스 안의 값이 유효한 지에 따라 함수가 실행되니 함수 합성을 안전하게 할 수 있다.
Promise.resolve(1).then(g).then(f).then(r => console.log(r));
// output
// 4
Promise는 값이 없는 경우가 아닌 값이 미래에 준비될 것으로 예상되는 경우에 함수 합성을 안전하게 하기 위해 사용된다.
'JavaScript' 카테고리의 다른 글
[FP&ES6+] go, pipe, reduce에서 비동기 제어 (0) | 2022.01.08 |
---|---|
[FP&ES6+] Klesli Composition 관점에서의 Promise (0) | 2022.01.05 |
[FP&ES6+] 일급 값으로의 Promise 활용 (0) | 2022.01.03 |
[FP&ES6+] callback, Promise를 이용한 비동기 동시성 프로그래밍 (0) | 2022.01.02 |
[FP&ES6+] L.flatMap, flatMap (0) | 2021.12.28 |
댓글