질문은 :
- 생성기는 기능적 프로그래밍 패러다임을 파괴합니까? 그 이유는 무엇?
- 그렇다면 생성기를 기능 프로그래밍에 사용할 수 있습니까?
다음을 고려하세요:
function * downCounter(maxValue) {
yield maxValue;
yield * downCounter(maxValue > 0 ? maxValue - 1 : 0);
}
let counter = downCounter(26);
counter.next().value; // 26
counter.next().value; // 25
// ...etc
이 downCounter
방법은 상태 비 저장으로 나타납니다. 또한 downCounter
동일한 입력으로 호출 하면 항상 동일한 출력이 발생합니다. 그러나 동시에 전화next()
하면 일관된 결과가 생성되지 않습니다.
이 예제 counter
에서는 생성기 객체이므로 생성자가 함수형 프로그래밍 패러다임을 깨뜨릴 지 여부가 확실하지 않으므로 호출 next()
하면 정확히 동일한 생성기 객체와 동일한 결과가 생성됩니다 maxValue
.
또한 someCollection[3]
배열을 호출 하면 항상 네 번째 요소가 반환됩니다. next()
마찬가지로 생성기 객체에서 네 번 호출 하면 항상 네 번째 요소가 반환됩니다.
더 많은 맥락을 위해 프로그래밍 카타 작업을하는 동안 이러한 질문이 제기되었습니다 . 질문에 대답 한 사람은 생성기가 함수형 프로그래밍에 사용될 수 있는지 여부와 상태를 유지하는지 여부에 대한 질문을 제기했습니다.
답변
발전기 기능은 특별히 특별하지 않습니다. 콜백 기반 스타일로 생성기 함수를 다시 작성하여 비슷한 메커니즘을 직접 구현할 수 있습니다.
function downCounter(maxValue) {
return {
"value": maxValue,
"next": function () {
return downCounter(maxValue > 0 ? maxValue - 1 : 0);
},
};
}
let counter = downCounter(26);
counter.value; //=> 26
counter.next().value; //=> 25
분명히, downCounter
그것이 얻는만큼 순수하고 기능적입니다. 여기에는 문제가 없습니다.
JavaScript가 사용하는 생성기 프로토콜에는 가변 객체가 포함됩니다. 필요하지 않습니다. 위 코드를 참조하십시오. 특히 변경 가능한 객체는 참조 투명성 을 잃어 버립니다 . 즉 표현식을 값으로 대체 할 수 있습니다. 내 예에서,하지만 counter.next().value
것입니다 항상 로 평가 25
한 시점에서이 -이 발생하고 얼마나 자주 우리가 그것을 반복 상관없이, 이것은 JS 발생기 경우가 아니라 26
다음 25
, 그것은 정말 모든 숫자가 될 수 있습니다. 생성기에 대한 참조를 다른 함수에 전달하면 문제가됩니다.
counter.next().value; //=> 25
otherFunction(counter); // does this consume the counter?
counter.next().value; // what will this be? It depends on the otherFunction()
따라서 발전기는 상태를 유지하므로 “순수한”기능 프로그래밍에 적합하지 않습니다. 다행스럽게도, 순수한 함수형 프로그래밍을 수행 할 필요는 없으며 실용적 일 수 있습니다. 생성기가 코드를 명확하게 만들면 양심이 나쁜 상태에서 사용해야합니다. 결국 JavaScript는 Haskell과 달리 순수한 기능 언어가 아닙니다.
그건 그렇고, Haskell에서는 게으른 평가를 사용하기 때문에 목록과 생성자를 반환하는 것에는 차이가 없습니다.
downCounter :: Int -> [Int]
downCounter maxValue =
maxValue : (downCounter (max 0 (maxValue - 1)))
-- invoke as "take n (downCounter 26)" to display n elements