본문 바로가기

Backend/함수형 자바스크립트

Partial.js (3)

이전 포스팅 참고

 

이번엔 Partial.js 의 약간은 잡기능(?) 에 대해 알아보도록 하겠습니다.

 

- 고차 함수의 보조 함수에서 더 많은 인자 사용 

 

Partial.js 의 주요 고차 함수들에게는 지난 포스팅에 적은 비동기 제어 외에도 Underscore.js, Lodash 의 고차 함수와는 다르게 동작하는 부분이 있습니다. 그 중 하나인 보조 함수에서 더 많은 인자를 사용하는 부분을 예시 코드로 보도록 하겠습니다.

 

const _ = require('underscore');

function test1() {
  const a = 1;
  const b = 1;
  
  return _.map([1, 2, 3], val => val * a - b);
}

console.info(test1()); // [-1, 0, 1]

 

먼저 Underscore.js 에선 클로저를 사용해서 a, b 를 기억하게 만들고 이를 _.map 에서 사용하도록 코드를 작성해야 합니다.

 

const _ = require('partial-js');

const test1 = _.map(1, 2, [1, 2, 3], (a, b, val) => val * a - b);

console.info(test1); // [-1, 0, 1]

 

Partial.js 에서 _.map 은 인자를 여러 개 넘기면 iteratee 에도 인자로 넘겨주고, 이를 사용할 수 있습니다.

코드를 더욱 깔끔하게 만들어주는 장점이 있습니다.

 

- _.all / _.spread

 

이 두 함수는 파이프라인과 사용할 때 유용합니다. 예시 코드로 보도록 하겠습니다.

 

_.go(10,
  _.all(
    a => a + 5,
    a => a - 5,
    a => a * 5,
  ),
  console.info); // 15 5 50

 

_.all 함수는 모든 인자를 모든 함수들에게 동일하게 전달합니다. 따라서 위 함수에서 각 세 함수의 a 에는 전부 10이 들어가게 됩니다.

 

_.go(_.mr(10, 9, 8),
  _.spread(
    a => a + 5,
    a => a - 5,
    a => a * 5,
  ),
  console.info); // 15 4 40

 

_.spread 함수는 받은 인자를 하나씩 함수에 나눠줍니다. 따라서 여기선 세 함수에 각각 10, 9, 8 이 들어가게 됩니다.

예시를 위해 억지로 _.mr 함수를 사용했지만, 위의 _.all 과 함께 이런식으로 사용할 수도 있습니다.

 

_.go(10,
  _.all(
    a => a + 5,
    a => a - 5,
    a => a * 5,
  ),
  _.spread(
    a => a + 5,
    a => a - 5,
    a => a * 5,
  ),
  console.info); // 20 0 250

 

_.all 과 _.spread 는 동기로만 동작하지만 _.go, _.pipe 등의 파이프라인 함수와 함께 비동기 상황에서도 유용히 사용할 수 있습니다.

 

- _.indent

 

자바스크립트에선 스코프라는 개념이 있어서, 자식 스코프에선 부모 스코프의 지역 변수를 참조할 수 있씁니다.

이러한 접근 방식이 파이프라인 내에서도 필요한데, 이를 가능하게 해주는 함수가 _.indent 입니다.

_.indent 함수는 이름처럼 _.indent 가 중첩될 때마다 this 와 arguments 를 한 단계씩 안으로 들여쓰는 컨셉을 갖고 있습니다.

 

const f1 = _.indent(
  function() {
    console.info(this, arguments); // { parent: global, arguments: [1, 2] } [1, 2]

    return 'test1';
  },
  function() {
    console.info(this, arguments); // { parent: global, arguments: [1, 2] } ['test1']
  }
);

f1(1, 2);

 

_.indent 는 _.pipe 처럼 함수를 리턴하는 파이프라인입니다.

저는 Node.js 환경에서 실행시켰기 때문에 parent 가 global 로 나왔지만, 브라우저 환경에선 Window 로 나올 것입니다.

여기서 찍은 두 console.info 에는 arguments 의 차이가 있습니다.

첫 번째 함수는 이 파이프라인의 첫 함수이므로 f1(1, 2) 을 통해 넘겨진 인자 [1, 2] 가 arguments 에 들어옵니다.

두 번째 함수에선 이전 함수가 'test1' 을 리턴하기 때문에 ['test1'] 이 arguments 로 들어오게 됩니다.

 

다만 두 함수에서 this 가 들고있는 값은 똑같은 것을 볼 수 있습니다.

파이프라인에서 코드를 짜다가 중간에 있는 함수에서 최초로 받은 인자를 알고 싶을 경우, _.indent 를 사용하게 되면 어디서든 this.arguments 를 통해 최초 인자들에 접근할 수 있습니다.

물론 _.indent 를 사용하지 않아도 클로저를 사용해 충분히 해결 가능하지만, 좀 더 쉬운 방법이긴 합니다.

 

_.indent 의 this 는 한 번의 파이프라인 사용 후 휘발되는 this 입니다.

Express.js 에서 req 나 res 를 사용하듯이 값을 이어주는 식으로 사용할 수도 있고, 함수를 실행할 때마다 새로 생겼다가 사라지는 this 이므로 부수 효과가 최소화되며 메모리 사용에도 문제가 없습니다.

 

this 의 등장으로 인해 약간 의구심이 들 수 있는 부분을 예시와 함께 보도록 하겠습니다.

 

const f2 = _.indent(
  function(a) {
    this.b = a + 10;
  },
  function() {},
  function() { console.info(this.b); });
  
f2(5); // 15
f2(10); // 20

 

이런 코드를 보면 this 의 값을 변경하고 있기 때문에, 함수형 프로그래밍과는 본질이 다르게 상태를 변경해 나가는 프로그래밍이라는 생각이 들 수 있습니다. 하지만 _.indent 에서의 this 는 앞서 얘기한 것 처럼 작은 함수들을 모아 만든 하나의 파이프라인에서 만들어지는 this 이므로 외부 상태라고 볼 수 없으며 지속되는 this 가 아니기에 부수 효과 없는 코드 작업이 가능합니다.

 

const test1 = _.indent(
  function(a) {
    this.b = a + 10
  },
  _.indent(
    function() {
      this.b = 30;
      console.info(this.b); // 30
      console.info(this.parent.b); // 20
    },
    function() {
      console.info(this.parent.arguments); // [10]
    },
  )
);

test1(10);

 

위 예시를 통해 _.indent 를 두 번 이상 사용할 때 this 의 구조를 알 수 있습니다.

this 의 parent 및 arguments 를 통해 접근이 가능한데, 사실 이 방법은 언제 어디서든 값을 변경할 수 있다는 말도 되므로 값을 불변적으로 다루는 것이 중요합니다. 이 부분은 다른 포스팅에서 얘기해보도록 하겠습니다.

 

- _.async 

 

지금까지 얘기한 것 처럼 _.go, _.pipe, _.indent 는 파이프라인 내부의 함수에서 비동기 결과가 나올 경우 비동기 제어를 시작하도록 구현되어 있습니다. _.async 는 내부 함수의 결과와 상관없이 무조건 비동기를 일으키고 싶을 때 사용합니다.

 

_.go.async(1, a => a).then(console.info);
console.info(2);

// 2
// 1

 

- _.branch : 비동기 건너뛰기

 

이런 방법을 써야할 경우가 언제일지 구체적인 예시는 떠오르지 않지만, _.go 를 이용하면서 중간에 비동기 결과가 나오더라도 비동기 제어를 하지 않는 방법이 있습니다.

 

_.go(20,
  _.branch(
    a => {
      return new Promise(resolve => resolve(a + 10));
    },
    a => {
      console.info(a);
      return a * a;
    },
    console.info,
  ),
  console.info);
  
// 20
// 30
// 900

 

원래대로면 30, 900 이 찍힌 후 20 을 찍도록 내려와야 하지만, 여기선 _.branch 를 사용함으로써 20 이 먼저 찍혔습니다.

_.branch 내부에선 비동기 제어가 되지만 결과를 기다리지 않고 바로 내려옵니다.

딱히 떠오르는 예시는 지금 없지만, 파이프라인을 사용하더라도 동기적으로 결과를 바로 반환해야 하는 함수들과 비동기 함수들이 협업할 경우 _.branch 를 사용할 수 있을 것 같습니다.

 

도입부에선 Partial.js 의 잡기능(?) 이라고 소개하긴 했지만 꽤 유용한 기능들이며, 개인적으로 _.indent 는 잘 활용할지 모르겠지만 _.all, _.spread, _.branch 와 같은 함수들은 이후 프로그래밍에서 좀 더 재밌는 코드를 만들 수 있지 않을까 기대해봅니다.

'Backend > 함수형 자바스크립트' 카테고리의 다른 글

지연 평가 (2)  (0) 2021.10.23
지연 평가 (1)  (0) 2021.10.17
Partial.js (2)  (0) 2021.10.02
Partial.js (1)  (0) 2021.09.25
함수 조립 (2)  (0) 2021.09.18