본문 바로가기

Study/VanillaJS

[개념잡기] 함수형 프로그래밍 - (7) go, pipe

[개념잡기] 함수형 프로그래밍 - (7) go, pipe

서론

함수를 받아서 함수를 리턴하는 pipe 함수와, pipe 의 즉시 실행 함수형 go 함수에 대해 알아보자

함수형 프로그래밍

pipe

pipe 함수는 인자로 함수리스트를 받아 return 값을 다음 함수로 넘겨 결과로 함수를 리턴하는 함수이다. 기대하는 동작은 다음과 같다.

var _pipe = function(){}

var pipe = _pipe(function(a){return a + 1},function(a){return a * a}) 

console.log( pipe(1) ) // 4 (a + 1) * a 

pipe 함수를 구현해보자. reduce를 이용하여 순서대로 함수들을 수행해가면서 return 값을 다음 함수로 넘겨주는 코드로 작성해면 될 것같다.

const _pipe = function() {
  var fns = arguments
  return function(arg) {
    return _reduce(fns, function(arg, fn){
      return fn(arg)
    }, arg)
  }
}

var pipe = _pipe(function(a){return a + 1},function(a){return a * a}) 
console.log( pipe(1) ) // 4

go

go 함수는 pipe 함수의 즉시실행하는 케이스로 첫번째 인자로 초기값을, 두번째 인자부터 함수들의 리스트를 받아 함수리스트들을 pipe 함수에 인자로 넘기고 첫번째 인자로 받은 초기값으로 함수를 즉시 실행시킨다. 기대하는 동작을 보자.

const _go = function(){}

_go(1,
   function(a){return a + 1},
   function(a){return a * a},
   console.log) // 4

go 함수를 구현해보자 pipe 함수를 이용하여 인자로 받은 함수리스트를 넘겨주면 될 것같다.

const _go = function() {
  [arg, ...fns] = arguments
  return _pipe.apply(null, fns)(arg)
}

여기서 apply 함수는 전 포스팅에서 다뤘던 call 함수와 같은 역할을 하는데 차이점은 call은 파라미터를 하나씩 넘겨야 하지만 apply 는 배열형태로 파라미터를 넘길 수 있다.

코드 개선(1)

지난번까지 했던 30세 이상 유저의 이름을 가져오는 코드를 보자.

console.log(
  _map(
      _filter(users, function(user){ return user.age >= 30 }),
      _get("name")
  )
) // (4) ["QW", "WE", "ER", "AS"]

이 코드를 오늘 만든 go 함수를 이용하여 개선해보자.

_go(users,
  function(users) {
    return _filter(users, function(user){ 
      return user.age >= 30 
    })
  },
  function(users) {
    return _map(users, _get("name"))
  },
  console.log
) // (4) ["QW", "WE", "ER", "AS"]

전에 작성한 코드는 뭔가 안에서부터 밖으로 나가는 코드 느낌이라면, 이번에 작성한 코드는 체이닝하는 느낌이 들어 더 직관적이다.

위에 코드를 보면 공통점이 있다. users 배열을 받아 로직을 처리한후 값을 리턴한 다는 것이다. 이때 users 를 받는 평가시점을 늦추면 좀더 코드를 매끄럽게 작성할 수 있을것 같다. 그러기 위해 map, filter 함수에 curryr 함수를 적용시켜 코드를 다시 한번 작성해보겠다.

const _filter = _curryr((list, predi) => {
  var new_list = []
  _each(list, (val) => {
    if(predi(val)) {
      new_list.push(val)
    }
  })
  return new_list
})

const _map = _curryr((list, mapper) => {
  var new_list = []
  _each(list, (val) => {
    new_list.push(mapper(val))
  })
  return new_list
})

_go(users,
  _filter(user => user.age >= 30), // user
  _map(_get("name")),
  console.log) //(4) ["QW", "WE", "ER", "AS"]

훨씬 코드가 간결해진 것을 볼수 있다.

마무리

// 기존에 작성했던 코드
console.log(
  _map(
    _filter(user, function(user){ return user.age >= 30 }),
    function(user) { return user.name }
  )
)

// 개선한 코드
_go(users,
  _filter(user => user.age >= 30), // user
  _map(_get("name")),
  console.log) //(4) ["QW", "WE", "ER", "AS"]

지금까지 명령형 코드를 함수형 코드로 작성해보았다.