함수형 프로그래밍이란?
순수 함수(pure function)들로만 구현하는 프로그래밍 방법. 순수 함수는 부수 효과(side effect)가 없으며, 따라서 함수형 프로그래밍은 부수효과가 없는 함수들로만 프로그램을 구축한다는 의미입니다.
부수효과(Side effect)
함수에서 결과를 돌려주는 것 이외의 작업을 가리켜 부수효과라고 합니다. "결과를 돌려주는 것 이외의 작업"은 외부세계를 변경하는 작업을 말합니다. 즉 함수 밖의 상태를 변경하는 모든 작업을 부수효과라 부릅니다.
부수효과의 예
- 참조에 의한 파라미터를 수정
- 예외를 던지거나 오류를 발생시키면서 실행을 중단
- 콘솔에 파일에 읽거나 쓰기(I/O)
- 서버와 통신
참조 투명성(Referential Transparency)
순수 함수를 참조 투명성이란 개념으로 공식화할 수 있습니다. 참조 투명성은 함수가 아니라 표현식(expression)의 한 속성입니다. 표현식은 프로그램을 구성하는 코드 중 하나의 결과로 평가(evaluation) 될 수 있는 코드 조각이라고 보시면 됩니다.
참조 투명성과 순수성
모든 프로그램 p에 대하여 임의의 표현식 e를 모두 e의 평과 결과로 치환 하더라도 p의 의미에 아무 영향을 미치지 않는다면, 표현식 e는 참조에 투명하다. 만일 표현식 f(x)가 참조에 투명한 모든 x에 대해 참조에 투명하다면, 함수 f는 순수하다.
예를 들어 "2 + 3" 은 하나의 표현식이고 이 표현식의 평가 결과는 "5" 입니다. 프로그램에 있는 모든 "2 + 3"을 "5"로 변경하더라도 프로그램의 동작은 동일합니다. 이러한 특성을 가질 때 참조에 투명하다고 말합니다. 말 그대로 참조하는 값이 임의로 변경되지 않는다는 의미입니다.
그렇다면 참조 투명하지 않은 경우를 살펴보겠습니다.
val x = new StringBuilder("Hello")
val y = x.append(", World")
val r1 = y.toString
val r2 = y.toString
println(r1) // Hello, World
println(r2) // Hello, World
위 프로그램에서 r1과 r2 값은 동일합니다. StirngBuilder의 append 함수가 참조 투명한지 확인하기 위해서 y를 x.append(", World")로 치환해보겠습니다.
val x = new StringBuilder("Hello")
val r1 = x.append(", World").toString
val r2 = x.append(", World").toString
println(r1) // Hello, World
println(r2) // Hello, World, World
수정한 프로그램을 실행하면 r1과 r2 값이 다르게 출력됩니다. append 함수가 호출 될 때 x 객체를 변이시키기 때문입니다. 따라서 append 함수는 순수 함수가 아니란 것을 확인할 수 있습니다.
함수형 프로그래밍을 해야하는 이유?
불순 함수는 동일한 입력에 대해서도 상황에 따라 다른 출력을 낼 수 있고 외부세계의 값을 바꾸기도 합니다. 이러한 예측불가성은 프로그램의 복잡성을 증가시킵니다.
반면에 순수 함수는 동일한 입력에 대해 동일한 결과를 보장하고 외부 세계에 변화를 일으키지 않습니다. 이러한 순수함수의 특성은 모듈성을 증가시킵니다. 모듈성이 증가하면 다음과 같은 장점을 가집니다.
- 재사용하기 쉽다.
- 버그를 찾기 쉽다.
- 테스트를 코드를 쉽게 작성할 수 있다.
- 스레드와 같은 동시/병행 작업을 안정적으로 만든다.
References
- Paul Chiusano and Runar Bjarnason, 『Functional Programming in Scala』, Manning Publications(2014)