인프런 커뮤니티 질문&답변

withkey님의 프로필 이미지
withkey

작성한 질문수

함수형 프로그래밍과 JavaScript ES6+

Kleisli Composition - L.filter, filter, nop, take

평가 순서에 대해 질문이 있습니다!

작성

·

326

0

안녕하세요.

 

먼저 좋은 강의와 항상 빠르게 답변을 해주셔서 너무 감사합니다.

질문은 아래와 같습니다.

지연성 1 파트에 있는 평가순서 강의를 복습하면서 클레이슬리 컴포지션의 평가 순서를 생각해보았는데, 제 생각이 맞는지 궁금합니다.

 

예시코드

// go

go(
    [1, 2, 3, 4, 5],
    L.map((n) => Promise.resolve(n)),
    L.filter((n) => n % 2), 
    take(2), 
    console.log // [1, 3]
  );

// take

export const take = curry((l, iter) => {
  let result = [];
  iter = iter[Symbol.iterator]();

  // recur()가 실행되면, 여기서 다시 시작을 하는데.
  // iter.next()가 실행되면,
  return (function recur() {
    let cur;
    while (!(cur = iter.next()).done) {
      const value = cur.value;

      // a가 promise 인 경우 재귀함수를 이용해서 처리함
      if (value instanceof Promise)
        return (
          value
            //
            .then((v) =>
              (result.push(v), result).length === l ? result : recur()
            )
            // filter에서 reject가 되면, catch에 걸리고 e가 nop이면 recur()를 실행한다.
            .catch((e) => (e === nop ? recur() : Promise.reject(e)))
        );
      result.push(value);
      if (result.length === l) return result;
    }

    return result;
  })();
});

// L.filter

const nop = Symbol("nop");

export const L.filter = curry(function* (f, iter) {
  for (const a of iter) {
    const b = go1(a, f);
    if (b instanceof Promise) yield b.then((b) => (b ? a : Promise.reject(nop)));
    else if (b) yield a;
  }
});

// L.map

export const L.Map = curry(function* (f, iter) {
  for (const el of iter) {
    yield go1(el, f);
  }
});

 

평가 순서

  1. take 함수 내, iter.next() 평가를 시도 → L.filter로 이동

  2. L.filter 내, iter.next() 평가를 시도 → L.map 으로 이동

  3. L.map 내, inter.next() 평가를 시도한다. → [1, 2, 3..] 를 iter 로 받았기 때문에 1로 평가 된다.

  4. L.filter 로 돌아와, L.map으로부터 평가받은 Promise.resolve(1) 이라는 값(b)으로 내부 로직을 수행한다.

    1. b의 인스턴스가 Promise라면, b.then을 실행하고 a 또는 Promise.reject을 한다.

      1. 여기서 a는 Promise 이다.

    2. b의 인스턴스가 Promise가 아니라면, result.push(value); 을 실행하고 while로 순회한다.

  5. L.filter 의 평가가 끝나면 take 함수로 돌아온다

    1. take로 전해지는 L.filter 의 평가값은 4개 중에 하나일 것이다.

      1. a → take 내에서 result.push 가 실행됨

      2. Promise.resolve(a) → take 내에서 .then 절이 실행됨.

      3. Promise.reject(nop) → take 내에서 .catch 절이 실행되고 recur() 를 실행함

      4. Promise.reject(e) → take 내에서 .catch절이 실행되고, Promise.reject(e) 로 이어짐

    2. recur()가 실행되면, take 에서 실행된 첫번째 while 사이클이 끝나면서, 다시 recur()가 실행되며 iter.next()가 평가할때 위 과정을 다시 반복한다. take에 있는 while 사이클이 return되고 recur() 함수 실행이 종료됐음에도 iter.next()의 값이 이전값에서 이어지는 이유는 recur() 와 iter가 클로저이기때문이다.

 

 

 


답변 1

0

유인동님의 프로필 이미지
유인동
지식공유자

네 맞을거 같습니다!

유인동님의 프로필 이미지
유인동
지식공유자

엄청 디테일하게 잘 보셨네요!

withkey님의 프로필 이미지
withkey
질문자

앗 감사합니다. IIFE 라던가, recursive, 그리고 iter.next() 에서 평가가 될때마다 말씀하신 "세로"로 함수가 실행되는 부분이 조금 혼란스러워서 익숙해지려고 많이 연습을 했는데, 다행이네요 ㅎㅎ 강의가 너무 좋아요!! 감사합니다!!

유인동님의 프로필 이미지
유인동
지식공유자

❤️

withkey님의 프로필 이미지
withkey

작성한 질문수

질문하기