본문 바로가기
Study

함수형 프로그래밍(Functional Programming)

by DawIT 2021. 12. 28.
320x100

개요

위키백과에서는 함수형 프로그래밍을 다음과 같이 정의하고 있다.

 

함수형 프로그래밍은 자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임의 하나이다.  
명령형 프로그래밍에서는 상태를 바꾸는 것을 강조하는 것과는 달리, 함수형 프로그래밍은 함수의 응용을 강조한다. 프로그래밍이 문이 아닌 식이나 선언으로 수행되는 선언형 프로그래밍 패러다임을 따르고 있다.

 

객체 지향 프로그래밍은 모든 사물을 객체로 간주하고 그 객체간의 협력과 역할의 분배를 통해 인간의 사고와 비슷하게 코드를 작성할 수 있다는 점에서 매력적이었다. 그러나 객체 지향 프로그래밍의 하나의 문제는 객체가 상태를 가진다는 것에 있었다. 객체가 가지고 있는 상태가 어떻게 변하는지에 따라 무궁무진한 결과를 만들어 낼수 있었고, 이는 예상할 수 없는 버그와 예외상황을 만든다는 단점이 있었다.

 

그래서 개발자들은 함수형 프로그래밍 이라는 패러다임에 눈을 돌리게 되었다. 상단의 위키의 정의에도 나와 있듯, 함수형 프로그래밍의 기본적인 원칙은 '상태와 가변 데이터를 멀리한다' 는 것이다. 이를 통해 디버깅이 수월하고 훨씬 더 유지보수하기 쉬운 코드를 작성할 수 있게 되는 것이다.

 

선언적 프로그래밍

함수형 프로그래밍은 선언적으로 작성되어야 한다.

 

선언적 프로그래밍이라는 것은 코드를 작성할때 어떤 방법(How)에 집중하기보다 무엇(What)을 이룰지에 대해 집중하는 프로그래밍을 이야기한다. 이에 반하여 객체지향 프로그래밍은 명령형, 절차적 프로그래밍으로서, 방법에 더 집중한다.

 

const arr = [6,8,2,7]
for (let i = 0; i < arr.length; i++){
  arr[i] = arr[i] * 10;
}

 

명령형 프로그래밍으로 배열의 각 요소에 10을 곱하는 코드를 작성하면 위와 같을 것이다. 명령형 프로그래밍에서는 해당 작업을 어떻게 진행하는지에 대해 작성한다.

 

const arr = [6,8,2,7]
const newArr = arr.map((element) => element * 10)

 

 

명령형 프로그래밍에서 변수를 선언하고 값을 늘리면서 10을 곱하는 모든 과정을 작성한 것과 달리, 함수형 프로그래밍에서는 무엇을 할지에 대해 표현하는것에 더 관심이 있다. 각 배열의 요소를 돌면서 10을 곱한다는 것에 대한 표현만 작성하여 코드가 훨씬 간결해졌다.

 

순수 함수(pure function)

함수형 프로그래밍에서 함수는 순수 함수여야 한다.

 

순수 함수란, 일반적으로 동일한 입력에 대해 항상 같은 결과를 반환(참조 투명성)하고, 부작용(부수 효과,Side Effect)이 없는 함수를 이야기한다. 여기서 부작용이란, 해당 함수의 실행으로 해당 함수 외부에 영향을 끼치거나 영향을 받는 것을 말한다. 가장 대표적인 부작용으로 변수의 값 변경이나 객체의 맴버 변수의 수정 등을 들 수 있겠다. 객체지향 프로그래밍이라면 당연하게 할 수 있는 것들은 순수 함수에서는 허용되지 않는다. 순수 함수는 그저 어떠한 목적을 수행하기 위해 인풋을 사용하여 아웃풋을 만드는 연산만을 진행한다.

 

let number = 1;

function increase(amount){
  number += amount
}

 

상단 함수는 순수 함수가 아니다. increase 함수는 외부 전역 변수인 number의 값을 변화시킨다.

 

function now(){
  console.log(new Date())
}

 

Javascript의 Date 객체를 생성하는 시점(외부 요인)에 따라 다른 결과를 만들기 때문에 상단의 함수도 순수 함수가 아니다. 이렇게 여러번 호출했을때 다른 결과가 나오는 함수는 멱등성이 보장되지 않는다고 한다. 또한 console에 log를 찍는 행위도 부수 효과이다.

 

function add(a,b){
  return a + b;
}

 

상단의 add 함수는 순수 함수라고 이야기할 수 있다. 외부에 아무 영향도 끼치지 않으며, add에 x와 y를 넣었을 때 항상 같은 결과를 반환하기 때문이다.

 

이러한 순수 함수를 사용하면, 외부에 영향을 끼치거나 받지 않기 때문에 스레드 안전성을 보장받을 수 있다. 그래서 병렬 처리를 동기화 없이 수행할 수 있다는 장점도 가지고 있다.

 

일급 시민으로서의 함수

함수형 프로그래밍에서는 함수는 일급 시민(First Class Citizen)으로 취급받아야 한다. 이에 대해 알아보기 전에 먼저 일급 시민이란 무엇일까?

 

일급 시민은 다음 세 가지 조건을 만족한다.

 

  • 변수나 데이터로 사용할 수 있다.
  • 객체의 인자로 사용할 수 있다.
  • 갹체의 반환값으로 사용할 수 있다.

 

위 조건을 모두 만족해야만 일급 시민으로 사용할 수 있는 것이다. 이제 '함수가 일급 시민으로 사용된다' 는 말은 저 세 가지 조건의 앞에 '함수를' 을 넣으면 된다.

 

  • 함수를 변수나 데이터로 사용할 수 있다.
  • 함수를 객체의 인자로 사용할 수 있다.
  • 함수를 객체의 반환값으로 사용할 수 있다.

 

대표적으로 자바스크립트의 함수가 이 조건을 모두 만족하기 때문에 1급 시민이며, 자바의 경우 단일 추상 메서드만을 사용하는 함수형 인터페이스를 통해 이를 구현할 수 있다.

 

불변성

함수형 프로그래밍에서는 불변성을 지키며 개발해야 한다. 불변성이란 말 그대로 한번 생성된 값이 변하지 않음을 뜻한다. 공유 자원을 최소화해야 하는 함수형 프로그래밍에서는 값을 사용하기 위해 값의 변경이 일어나야 한다면 새로운 객체를 만들고 변경된 값을 할당한다. 이는 무분별한 값에 접근과 수정으로 인해 디버깅이 힘들어지는 상황과 부작용을 막기 위함이다.

 

얻을 수 있는 장점

먼저, 병렬 처리에 강하다. 이전에 병렬 처리를 위한 기법인 뮤텍스와 세마포어에 대해 알아보았는데, 함수형 프로그래밍은 상태 변화와 공유 자원을 멀리하기 때문에 부작용이 없고, 이덕분에 스레드 안정성이 보장되어 이러한 기법을 쓸 필요도 없이 안전하고 빠르게 병렬 처리를 수행할 수 있다.

 

또한 함수형 프로그래밍으로 코드를 작성하면 디버깅과 유지보수에 용이하다. 어떠한 문제가 발생하면 문제가 발생한 함수만 살펴보면 해결책을 찾을 수 있다. 이는 함수가 다른 외부에 영향을 끼치거나 받지 않도록 설계되어 있기 때문에 얻을 수 있는 이점이다.

단점

어떻게 작성하는지에 따라 다르겠지만, 객체지향 프로그래밍에 비해 훨씬 많이 추상화되기 때문에 함수형으로 사고하는 연습을 하지 않는다면 코드를 한눈에 알아보기 어렵다.

 

또한 함수형 프로그래밍을 위한 자료구조들을 언어에 따라서 지원하지 않는 경우가 있기 때문에 이러한 자료구조들은 직접 구현을 해야한다는 단점도 존재한다.

 

참고자료

https://uzihoon.com/post/4de52810-5201-11ea-a189-4bd78d8bfce2

https://mangkyu.tistory.com/111

 

댓글