[프로그래밍 패러다임] 함수형 프로그래밍(FP)

유재경·2021년 6월 13일
0
post-thumbnail

정의

자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 기법이다.

객체지향 프로그래밍이 갖고 있는 한계를 보완하였습니다. 명령형, 객체지향 프로그래밍에서는 값이나 상태 변화를 중요시 여겼으며, 객체 간 데이터 교환 시 overhead를 발생시키거나 객체가 상태(state)를 가진다는 점에서 함수형 프로그래밍이 주목을 받게 되었습니다.

함수형 프로그래밍에서 따라다니는 용어들이 있습니다. 이 특징에 따라 장단점이 되기도 하는데요, 개념을 잘 이해해봅시다.

Currying

커링은 여러 개의 인자를 받받는 함수를 단일 인자를 받는 함수의 체인으로 바꾸는 방식을 말한다. 매 턴 마다 받을 수 있는 인자는 오직 한 개이다.

func f<A, B, C, D>(_ a: A, _ b: B, _ c: C) -> D {
	...
}

func f<A, B, C, D>(_ a: A) -> (B) -> (C) -> D {
	...
}

currying은 각 조건에 따라 별도로 중간에 함수를 만들 수 있으며, 함수를 합성할 수 있기 때문에 확장성이 좋아진다. 또한 호출할 때의 전달 인자가 겹치는 것을 피할 수 있다.

Partial Application

앞서 말한 currying(커링)과 공통적으로 함수를 반환하는 함수이지만, 인자가 반드시 하나일 필요는 없고 여러 개의 인자를 받을 수 있다. 사실, curry는 partial application의 특수한 형태이다. partial application은 미리 전달받아둘 수 있는 제한이 없는 반면, currying은 인자의 개수가 1개로 제한되기 때문이다.

partial application은 함수에서 여러 개의 인자를 한 번에 고정시켜두고 싶을 때 유용하지만, 함수를 조합할 경우 한계가 있다. 예를 들면, l = f(g(h))) 로 정의하고 싶지만, f함수에는 매개 변수 2개가 정의되어있는 경우에는 해당 식이 불가능하다. 이럴 때엔 curry가 무조건 전달인자를 1개만 받는다는 점에서 일관성이 있고 함수를 조합할 때에는 curry가 유용할지도 모른다.

Monad

프로그램을 구조화할 수 있는 추상화를 말합니다.

특징

Concurrency

CPU가 발전하다가 현실적인 벽에 부딪히게 되자, 수직적 전략이 아닌 여러 개의 칩을 병렬적으로 동작하게 하는 수평적인 전력을 취하게 됩니다. 변경 가능한 데이터(Mutable Data)는 Concurrency 환경에서 골칫덩어리(까진 아니고.. 곤란해지는 정도)가 되어버렸습니다. 왜냐하면 동시성 작업(Concurrent work)에 따라 값을 수정 중에 다른 스레드에서 이를 부르는 경우에 예상치 못한 값을 불러오게 되기 때문이죠. 즉, 데이터 무결성을 보장할 수 없게 되었습니다. 만약, lock 기법을 통해 무결성을 보장하고자 한다면 성능 저하와 복잡도 문제 때문에 쉽지 않았습니다.

그런데 이것을 함수형 프로그래밍의 immutable(데이터를 변경하지 않는) 특성이 Concurrency 환경에서의 데이터 무결성을 보장하도록 해주었지요.

함수형 프로그래밍 덕분에 값 변화를 멀리하므로 대규모 병렬처리, 멀티 코어 환경에서 효율적인 프로그래밍 가능해졌습니다.

No Side-Effect

Side-Effect에는 변수의 값이 변경됨, 자료 구조를 제자리에서 수정함, 객체의 필드값을 설정함, 예외나 오류가 발생하여 실행이 중단됨, 콘솔 또는 파일 I/O가 발생함 이 해당됩니다.

즉, Side-Effect가 없다는 것은 메모리나 I/O 관점에서 Side-Effect가 없다, 함수의 실행이 외부에 영향을 미치지 않는다 라고 정의할 수 있습니다.

이러한 Side-Effect를 제거한 함수들을 순수 함수(Pure Function)이라고 부르며, 함수형 프로그래밍에서는 이 순수 함수들로 구성됩니다. 순수 함수를 이용하면 함수 자체가 독립적이고 Side-Effect가 없기 때문에 보다 Thread-safety합니다. Thread-safety하다는 것은 병렬 처리를 동기화없이 진행할 수 있다는 것이죠.

Input-Output이 명확하므로 어떤 상황에서도 일정하게 같은 결과가 도출될 것을 보장합니다. 이는 개발자 입장에서도 동작이 예측되므로 훨씬 편해지겠죠.

Declarative(선언적)

함수형 프로그래밍이 선언형 프로그래밍에 속하는 데에는 a = f(b)로 정의하는 방식이기 때문입니다. 그리고 이 b에는 함수가 들어올 수 있다는 것이 특징으로 이 때 f를 고계함수라고 부릅니다. 이는 FP에서 함수를 '값'으로 간주하기 때문에 가능한 것입니다.

Input이 들어와 어떤 결과를 얻느냐에 초점이 맞춰져 있습니다. (고계함수란, 함수를 다루는 함수로 함수의 인자에 함수를 받거나 함수를 반환하는 함수를 말합니다.)

Operator의 조합으로 간결한 표현

함수형 프로그래밍에서는 일을 처리하는 동작이 추상화되고, 순수함수를 연결해서 결과를 도출하는 방식입니다. 하지만 Operator의 쓰임새와 순서를 잘 알아야 한다는 점에서 어느 정도 학습이 요구됩니다.

한계

함수형 프로그래밍에서는 Side-Effect도 줄이고 OOP에서 객체가 state를 가지는 문제점을 해결했지만, state가 없다는 것이 장점이자 단점이 되기도 합니다. OOP에서는 객체의 state가 있기 때문에, 코드로 표현하기에 더 직관적이었으나 FP에서는 같은 기능의 구현을 위해 다양한 함수의 조합을 만들어야 합니다. 결국 코드가 장황해질 수 있습니다.

(출처 https://devhue.github.io/blog/functional-programming
https://medium.com/오늘의-프로그래밍/함수형-프로그래밍-curry-와-partial-application-a7f83472cf53
https://soda1127.medium.com/2-03-함수형-프로그래밍-monad-edadbc8374aa
https://velog.io/@kmp1007s/함수형-프로그래밍의-Currying
https://blog.rhostem.com/posts/2017-04-20-curry-and-partial-application)

profile
iOS 개발

0개의 댓글