FP(Functional Programming) - 함수

함수

수학에서의 함수

함수는 두 집합 사이의 관계를 설명하는 수학적 개념 입니다.

아래 내용은 알아두면 좋습니다.

프로그래밍에서의 함수

function TEST(param: any){
	return true
}

두 개가 무슨 차이?

뭔가 받아서 뭔가 뱉어낸다의 맥락에선 일맥 상통 합니다.
그러나 수학에는 메모리가 없죠. 함수값은 함수값 입니다.

수학에서의 함수는 집합간의 관계를 설명하는 것이기 때문에 함수는 언제나 정확 합니다. 정의역의 원소를 넣으면 정확히 치역의 원소를 뱉어내게 됩니다. 정의역이 중간에 바뀌지도 않습니다.

그러나 프로그래밍은 모종의 이유(?) 로 인해 지금 실행하는 함수 단위에서가 아닌 다른 곳에서도 데이터에 접근하여 정의역을 바꿔 버릴수도 있습니다. 이렇게 되면 에러가 나겠죠

그래서 함수형 프로그래밍을 딱 검색 하면 제일 먼저 나오는게
순수함수 입니다.

순수함수

순수함수에 대하여 찾아보면 부수효과가 없는 어쩌고 저쩌고 이렇게 적혀 있습니다.
그런 관점 보다는 아래의 관점을 추천 드립니다.

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

이 함수의
정의역 : number 집합(타입)에 포함된 두 원소 a, b
치역 : number 집합(타입)에 포함된 연산 결과

입니다. 여담으로 number 집합(타입)은 군 이네요. + 연산에 닫혀 있으니까요!

여기서 순수함수라는 것은 무엇일까 생각 해 보겠습니다.

  • 정의역이 절대 바뀌지 않는다
  • 정의역이 바뀌지 않으면 치역은 정의역에 따라 항상 매핑된다

위의 함수는 정의역이 바뀔 것이 없지요. 그래서 순수함수 입니다.

const c: number = 10;
function add(a: number, b: number): number {
	return a + b + c;
}

function print(a: number, b: number): number{
    console.log(a)
    console.log(b)
    return a + b;
}

위의 함수는 어떨까요?
c가 primitive가 아니라서 안심 되시나요? c가 const고 number 집합의 원소이니
그냥 해도 상관 없을까요?

엄밀히 말해서 아닙니다.
c는 정의역에 포함된 원소가 아닙니다. 정의역에 포함 되있지도 않은 원소가 함수의 매핑 과정에 껴들었습니다. 그래서 순수함수가 아닙니다.

print도 외부 함수 console.log를 호출 했기 때문에 순수함수가 아니겠네요.

길게 얘기 했지만 한 문장으로 정리하면 아래와 같습니다.

함수의 파라미터를 변경 하지도 않고, 함수 안에서 외부 변수와 소통하지 않는 함수

가 순수함수라고 할 수 있겠습니다.

js에서 함수는 값으로 취급 가능 합니다.

js에서 함수는 일급 객체로 지정되어 있습니다. 일급 객체는 아래와 같은 조건이 있습니다.

  • 변수, 데이터 구조에 할당 가능
  • 파라미터 값으로 전달 가능
  • 리턴값으로 사용 가능

아래 예를 보겠습니다.

함수 선언식(은 호이스팅이 안되용!)
const testFunc = () => {} 
const testFunc = function(){}

const function(f: () => {}): Function{
	return () =>{}
}

이렇게 사용할 수 있다는 겁니다. 간단하쥬?

함수의 합성

우리는 전 글에서 계속 함수는 합성 가능하다고 얘기 했는데요. 수학은 그렇게 쓰고 개념적으로 알면 되지만 프로그래밍에서는 함수를 어떻게 합성 할까요?

function A(): number{
  return 10;
}
function B(param: number): number{
  return param + 10;
}

B(A()) 똑같네요!

이것이 함수의 합성 입니다.

그럼 함수 개수가 여러개면 어떻게 해요? 다 써요?

두개는 편하게 표현 가능한데 3개면? 4개면? A(B(C(D()))) 이렇게 되겠네요 .. 보기도 쉽지 않고..(괄호 보는데 눈이 아픕니다) 몇가지 함수관련 유틸 함수가 있습니다

currying

커링은 함수 합성은 아니지만 마땅히 주제 잡을게 없어서 여기다 썻어요.
함수 실행을 의도적으로 지연할 수 있다는 개념에서 출발 합니다. 이걸 지연 평가라고 하는데,
함수 실행을 평가한다 라고 표현 하나봅니다.

const f = curry(function(a, b, c){})
f(1)(2)(3)
f(1, 2)(3)

뭐 이런 식으로 작동하게 됩니다. 함수의 arguments 길이를 갖고 함수가 요구하는 파라미터 수가 입력 될 때 함수를 실행할 수 있습니다.
관련 자료가 많으니 찾아보면 금방 나옵니다.

pipe, go, compose

const f = pipe(functionn(){}, function(){}, function(){} ... )
f(20)

pipe 함수는 함수 배열을 합성한 함수를 실행하는 함수를 리턴 합니다.
go는 초기값을 처음에 갖고 함수를 바로 실행합니다.
compose는 수학적으로 표현되는 합성(오른쪽부터 진행한다고 했죠) 을 표현 합니다.

pipe는 A - B - C 합성이면 pipe를 구성 할 때 A, B, C 순으로 입력 하면 됩니다.
코드를 보면 직관적으로 이해할 수 있죠

compose는 A - B - C 합성이면 C, B, A 순으로 입력해야 합니다.

이건 뭐 큰 차이는 아닙니다.

함수 합성은 비동기를 지원하지 않습니다.

비동기를 지원하게 할 수 있습니다만. 내부에서 Promise를 생성해 연결하면 되겠죠?
그 순간 그 합성 함수는 비동기가 되어 함수 외부의 코드와 시점이 달라지게 됩니다.
(Promise는 실행하면 일단 pending 상태 Promise객체가 되고. then이나 await 으로 Promise 밖에서 알 수 있습니다. 그러나 await 같은 경우도 async 함수 안에서만 동기를 보장하기 때문에 실질적으로 동기와 비동기가 합쳐져 완전 동기가 된다는 것은 쉽지 않을 것 같습니다(안될거 같아요)

그래서 단순한 함수 합성 보다. 다양한 상황에서 함수를 합성할 수 있는데 그걸 모나드로 해결 합니다.

좋은 웹페이지 즐겨찾기