[함수형 프로그래밍] go, pipe 란 무엇일까?

Yoon Han·2022년 9월 17일
0
post-thumbnail

go?

함수형 프로그래밍을 공부하기 시작하니 시작부터 생소한 개념이 튀어나왔습니다.

go 라는 것인데, 공부해보니 뭔가 특별한 의미는 가진 것은 아니고 다음과 같은 일을 하는 함수라고 생각하면 될 것 같습니다.

첫 번째 인자를 초깃값으로 하여, 이 초깃값을 이후의 인자들(함수들)에 순차적으로 통과시켜 결과값을 내는 함수

조금 난해해 보이니 바로 코드로 구현해가며 차근차근 이해해보도록 하겠습니다.

go 구현하기

우선 go 는 함수니까 다음과 같이 정의해두고 시작하도록 하겠습니다.

const go = () => {}

그 다음 인자를 받도록 해줄건데, 첫 번째 인자는 초깃값이고 나머지 인자들은 전부 함수입니다. 이 때 초깃값을 제외한 나머지 인자들은 ... 연산자를 사용해서 묶어주겠습니다.

const go = (initialValue, ...fns) => {}

이제 함수 내부 구현을 할 차례인데요 초깃값을 fns 에 담긴 함수들에 하나씩 차례로 적용시키며 값을 누적시켜 나가는 일을 해야 합니다. 이 역할에 딱 맞는 함수가 있죠? 바로 reduce 입니다. 이전에 작성해 두었던 포스트에서 reduce 코드를 좀 가져오겠습니다.

function reduce(predicate, iter, acc) {
  if (acc === undefined) {
    // 3번째 인자인 acc 인자가 주어지지 않은 경우
    // 2번째 인자인 iter의 첫 번째 값을 초기 값으로 세팅한다.
    const iterator = iter[Symbol.iterator]()
    acc = iterator.next().value
  }
  // iter 를 순회하면서 각 item 과 acc 값에 대한 연산 결과를 acc 에 누적시켜 나간다.
  for (const item of iter) {
    acc = predicate(item, acc)
  }
  
  return acc
}

const go = (initialValue, ...fns) => {}

이제 이 reduce 를 가지고 go 함수를 구현해보겠습니다. go 함수 구현 부분에 집중해주세요.

const go = (initialValue, ...fns) => reduce((fn, acc) => fn(acc), fns, initialValue)

fns 에는 함수들이 배열 형태로 전달이 됩니다.

실행 흐름을 간단히 표현해보면 다음과 같겠네요.

fns[last]( ... ( fns[1]( fns[0](initialValue) ) ) )

구현한 go 함수 사용하기

자, 이제 구현한 go 함수를 직접 사용해 보겠습니다.

const result = go(
  0,
  a => a + 1,
  a => a * 10,
  a => a * 100
)

console.log(result) // 1000

위와 같이 깔끔하게 여러 함수의 연산결과를 쭉 이어서 작성 할 수 있습니다.

함수를 값으로 다루기 때문에 처음에는 많이 어색할 수 있습니다!
여러번 곱씹어 보면서 찬찬히 실행 흐름을 따라가 보세요.


pipe?

pipego 의 개념과 한 부분에서만 다릅니다. 바로 인자 부분인데요, 다음과 같은 일을 하는 함수 입니다.

모든 인자가 함수이며, 이 인자들(함수들)을 마치 하나의 관처럼 연결하여 주는 새로운 함수를 반환한다.

말로 풀어 설명하자니 역시 조금 난해하네요..

이 개념 또한 코드로 구현해가며 천천히 이해해보도록 하겠습니다.

pipe 구현하기

pipe 는 함수를 반환하는 함수라고 하였으니 다음과 같이 뼈대를 잡아줍니다.

const pipe = () => () => {}

인자는 전부 함수이고 여러개를 받을 수 있으므로 다음과 같이 작성해 줍시다.

const pipe = (...fns) => (...args) => {}

이제 반환되는 함수쪽을 구현해야 합니다.
반환되는 함수는 인자를 여러개 받을 수 있고, 받은 인자들을 fns 에 속한 함수들을 차례로 통과시킨(연산시킨) 값을 반환해야 합니다.

값을 받아서 해당 값을 차례로 통과시킨다.. 이건 위에서 구현한 go 함수를 사용하면 되겠죠?

const pipe = (...fns) => (...args) => go(...args, ...fns)

pipe 사용해보기

const pipedFunction = pipe(
    a => a + 1,
    a => a * 10,
    a => a * 100
)

console.log(pipedFunction(0)) // 1000

go 와의 차이점은 뭘까요?

바로 "계산의 지연성" 입니다.

go 함수는 그 즉시 초깃값에 대한 연산을 수행하지만,
pipe 를 통해서는 일단 함수만 받아놓고 해당 함수를 통해 나중에 연산을 수행할 수 있습니다.

profile
챗바퀴는 도는 삶이 싫은 프론트엔드 엔지니어

0개의 댓글