Как функциональный программист, я хочу, чтобы мой основной код не содержал побочных эффектов и переместил их на край приложения. ES2015 Iterator
s и Iteration Protocols
— многообещающий способ абстрагирования конкретных коллекций. Однако Iterator
также имеют состояние. Могу ли я избежать побочных эффектов, если буду полагаться исключительно на неизменяемые Iterable
?
Можно ли избежать побочных эффектов с помощью итераторов ES2015 и протоколов итераций?
Ответы (2)
Iterator
s вызывают наблюдаемые мутации
У итераторов есть одно существенное свойство: они отделяют потребителя от производителя 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
или, по крайней мере, использовать их с большой осторожностью.
[Symbol.iterator]()
, - это весь смысл протокола итерации.
- person Bergi; 12.10.2016
while(true)
может поместить вашу программу в бесконечный цикл - иногда намеренно, иногда нет - в любом случае, здесь есть некоторый риск, но я не думаю, что языки должны пытаться предотвратить использование людьми таких выражений. Мутации только наивно воспринимаются как всегда плохое — вызов итератора и локализация мутаций — вполне приемлемое/уместное использование интерфейса; и это не мешает вам писать функциональные, свободные от побочных эффектов программы.
- person Mulan; 12.10.2016
ref.next()
- это изменение состояния, и, на мой взгляд, это поведение довольно неявно. Этот пост может быть немного провокационным по тону, но только для того, чтобы подчеркнуть свою точку зрения. Я провел много времени с этим протоколом только для того, чтобы, наконец, понять, что я могу добиться аналогичного поведения, используя чистые функции, рекурсию и преобразователи, но без недостатков.
- person ; 12.10.2016
Чистый итератор очень прост. Все, что нам нужно это
- текущее значение
- замыкание, продвигающее итератор
- способ сигнализировать, что итератор исчерпан
- соответствующую структуру данных, содержащую эти свойства
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
является универсальным, то есть работает с итераторами любого типа данных.
Может ли кто-нибудь объяснить мне, почему нативные итераторы имеют состояние? Почему разработчик языка ненавидит функциональное программирование?
next
. - person   schedule 12.10.2016