강의

멘토링

커뮤니티

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

withkey님의 프로필 이미지
withkey

작성한 질문수

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

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

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

작성

·

371

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

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

네 맞을거 같습니다!

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

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

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

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

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

❤️

withkey님의 프로필 이미지
withkey

작성한 질문수

질문하기