Programming/Process & Multi Threading

Pure Function(순수 함수)는 왜 함수형 프로그래밍의 핵심 개념일까?

dev.pudding 2024. 2. 9. 15:57
728x90

자바스크립트로 함수형 프로그래밍(Functional Programming)을 공부하다가 처음 알게된 개념이다. Pure Function? 번역 그대로 순수 함수라는데 함수형 프로그래밍의 핵심 개념 중 하나라고 한다. 도대체 이게 왜 핵심개념 중 하나라는 건지 이해가 안되서 정리를 해보았다. 일단 공신력있는 위키피디아 정의를 알아보자

 

the function return values are identical for identical arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams, i.e., referential transparency), and the function has no side effects (no mutation of local static variables, non-local variables, mutable reference arguments or input/output streams).

 

'the function return values are identical for identical arguments' 특정 함수에 동일한 입력값을 전달 한다면, 해당 함수는 항상 동일한 결과값을 반환한다. 더 쉽게 말하면, 예측 가능한 입력값을 넣으면, 개발자가 예측한 상태로 결과값이 나온다는 뜻이다. 

 

 'the function has no side effects' 함수에 부작용이 없다? 여기서 말하는 부작용(side effect)는 함수가 호출될  때 외부에 영향을 미치는 것을 의미한다. 옆에 괄호 설명을 들여다보면, no mutation of local static variables, non-local variables, mutual reference arguments, I/O streams 써있다. 함수가 선언되고 종료되더라도 정적 변수,선언된 함수 외부의 변수,상호 참조하는 인자들, 출력 스트림에는 변형이 없다고(no mutation) 설명하고 있다. 

 

결론적으로 Pure Function(순수 함수)란 1)함수가 예측가능한 결과를 반환하고 2)외부 상태에 영향을 주지 않는 안정적인 함수라고 보면 될 것 같다. 

 

 Impure Function 라는 용어도 있는데 번역 그대로 비순수(Impure) 함수이다.  순수 함수의 반대 개념으로, 결과가 일관되지 않는 함수를 말한다.  

예시를 통해 비순수 함수(Impure Function)을 알아보자 

//함수 외부에 선언된 배열
const externalArr = [1,2,3];

//value 인자를 받는 impure 함수
const impure = value => {
  // result는 value값 + 외부 배열길이 
  let result = value + externalArr.length;
  //외부 배열에 result를 넣음 
  externalArr.push(result);
  //결과 출력 
  return result;
};

console.log(impure(4)); //7
console.log(impure(4)); //8

화살표 함수를 사용하여 value 인자를 받는 impure 함수를 선언하였다.  impure함수는 함수의 인자값인 value + 외부 배열인 externalArr의 길이를 반환한다.

함수가 실행될 때마다 externalArr 배열에 요소를 넣기 때문에 result의 값은 일관되지 않으며, 함수의 인자값이 동일하여도 결과값은 일정하지 않는 것을 볼 수 있다.

 

이제 예시코드인 비순수 함수를 순수함수 정의와 비교해보자

1)  the function return values are identical for identical arguments (동일한 인자값에 동일한 결과값이 나온다)

-> 예시코드에서는 impure(4)로 4라는 동일 인자값을 주었는데 결과가 일정하지 않았다. 

 

2) 'the function has no side effects' (함수는 부작용이 없다)

-> impure함수가 실행될 수록, 외부 배열인 externalArr에 값이 늘어났다. 즉 외부에 영향을 끼치고 있다(부작용이 있다)

 

이제 순수 함수(pure function)의 코드예시를 보자 

//외부 배열선언
const externalArr = [1,2,3];

//value,arr 인자값을 받는 pure함수 
const pure = (value,arr) => {
    //result는  value +  arr의 길이 
    let result = value + arr.length;
    //결과 반환 
    return result;
};

console.log(pure(4,externalArr)); //7
console.log(pure(4,externalArr)); //7

pure함수는 value와 arr 인자값을 받고, value와 arr의 길이를 더한 값을 반환한다. pure함수는 외부에 영향을 끼치지 않고 예측된 결과를 반환하는 함수로 말그대로 순수한 함수(pure function)이다 !  

 

물론 externalArr 배열에 요소를 push하면 결과값이 변할 수 있다. 따라서 아무리 순수함수로 정의하여도 외부에서 데이터를 변형시키면 결과값이 변할 수 있다. 특히 자바스크립트는 pure functional 환경이 아니기 때문에 순수 함수 로직이 지켜지지 않는 경우가 많은데, 함수형 언어인 Haskell이나 Scalar는 순수함수 로직이 지켜지기 위해 방어하는 로직이 있다고 한다. (필자는 두 언어 다 배워보지 못해서 예시는 생략하겠다..)

 

그래서 왜 순수 함수가 함수형 프로그래밍의 핵심인건가?

 

순수 함수의 장점을 종합하면, 

 

결과가 일관되어 병렬(parallel) 환경에서 호출할 수 있다. 

 

-> 순수 함수는 동일한 인자값으로 동일한 결과가 나타나며, 외부에 영향을 끼치지 않는다. 이를 멀티스레드 환경으로 생각해보면, 스레드간 서로 영향을 미치지 않는다는 것을 뜻한다. 즉 race condition이 발생하지 않는 환경을 만들어준다.

 

 순수 함수는 memoization이라는 컨셉이 유효하게 만들어준다.

 

-> memoization은 컴퓨터 알고리즘 용어로, 동일한 계산을 반복해야 할 경우 한 번 계산한 결과를 캐시 메모리에 저장해 두었다가 재사용하는 것이다.  순수함수에서는 입력에 대한 출력값이 항상 동일하기 때문에 캐시에 순수함수를 저장해두고 필요할때 바로 사용되어 프로그램의 속도를 향상시킨다. 

 

 순수함수는 Lazy Evaluation 컨셉이 유효하게 만들어준다.

 

-> Lazy Evaluation은 번역 그대로 느긋한 계산법이라는 뜻인데, 계산의 결과값이 필요할 때까지 계산을 늦추는 기법이다. 순수함수는 외부 상태를 변경하지 않고 입력값에만 의존하기 때문에 계산이 필요한 순간까지 함수를 호출하지 않는다. (이개념은 이해가 잘 안되서 더 공부해야겠다..)

 

 

 


참고 

https://www.freecodecamp.org/news/pure-function-vs-impure-function/

https://mingstone.tistory.com/entry/%EC%88%9C%EC%88%98-%ED%95%A8%EC%88%98%EC%99%80-%EB%B9%84%EC%88%9C%EC%88%98-%ED%95%A8%EC%88%98