作为一名功能性程序员,我希望保持主要代码免受副作用的影响,并将它们转移到应用程序的边缘。 ES2015 Iterator
和Iteration Protocols
是抽象特定馆藏的有前途的方法。但是,Iterator
也是有状态的。如果我完全依赖不可变的Iterable
,我还能避免副作用吗?
答案 0 :(得分:2)
Iterator
导致可观察到的突变迭代器具有一个基本属性:它们通过充当中介来将消费者与Iterable
的生产者分离。从消费者的角度来看,数据源是抽象的。它可能是Array
,Object
或Map
。这对消费者来说是完全不透明的。现在迭代过程的控制从生产者移动到Iterator
,后者可以建立拉动机制,消费者可以懒得使用。
要管理其任务,Iterator
必须跟踪迭代状态。因此,它需要是有状态的。这本身并无害处。但是,一旦可以观察到状态变化,它就会变得有害:
const xs = [1,2,3,4,5];
const foo = itor => Array.from(itor);
const itor = xs.keys();
console.log(itor.next()); // 0
// share the iterator
console.log(foo(itor)); // [2,3,4,5] => observed mutation
console.log(itor.next()) // {value: undefined, done: true} => observed mutation

即使您只使用不可变数据类型,也会出现这些效果。
作为一名功能性程序员,您应该避免Iterator
或至少小心使用它们。
答案 1 :(得分:2)
纯粹的迭代器很简单。我们所需要的只是
const ArrayIterator = xs => {
const aux = i => i in xs
? {value: xs[i], next: () => aux(i + 1), done: false}
: {done: true};
return aux(0);
};
const take = n => ix => {
const aux = ({value, next, done}, acc) =>
done ? acc
: acc.length === n ? acc
: aux(next(), acc.concat(value));
return aux(ix, []);
};
const ix = ArrayIterator([1,2,3,4,5]);
console.log(
take(3) (ix));
console.log(
ix.next().value,
ix.next().value,
ix.next().next().value)

任何地方都没有全球状态。您可以为任何可迭代数据类型实现它。 take
是通用的,它适用于任何数据类型的迭代器。
任何人都可以解释一下为什么原生迭代器是有状态的吗?为什么语言设计师讨厌函数式编程?