Можно ли избежать побочных эффектов с помощью итераторов ES2015 и протоколов итераций?

Как функциональный программист, я хочу, чтобы мой основной код не содержал побочных эффектов и переместил их на край приложения. ES2015 Iterators и Iteration Protocols — многообещающий способ абстрагирования конкретных коллекций. Однако Iterator также имеют состояние. Могу ли я избежать побочных эффектов, если буду полагаться исключительно на неизменяемые Iterable?


person Community    schedule 12.10.2016    source источник
comment
Если итерируемый объект неизменяем, какие побочные эффекты может иметь итератор?   -  person Bergi    schedule 12.10.2016
comment
Это все еще мультикаст, то есть вы можете поделиться эффектом next.   -  person    schedule 12.10.2016


Ответы (2)


Iterators вызывают наблюдаемые мутации

У итераторов есть одно существенное свойство: они отделяют потребителя от производителя 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 или, по крайней мере, использовать их с большой осторожностью.

person Community    schedule 12.10.2016
comment
Отличный вопрос, с отличным ответом. Кроме того, что такое преобразователь? - person evolutionxbox; 12.10.2016
comment
Кажется, итераторы побуждают людей изменять итерируемые объекты — я этого не вижу. Спецификация просто требует, чтобы они не ломались в случае их мутации во время итерации, но это остается плохой практикой. - person Bergi; 12.10.2016
comment
Да, никогда не следует делиться итераторами. Нужно поделиться итерируемым - возможность создать новый (локальный, собственный) итератор, вызвав [Symbol.iterator](), - это весь смысл протокола итерации. - person Bergi; 12.10.2016
comment
@Bergi Люди будут использовать протокол итерации по своему усмотрению. Я думаю, что хорошая языковая функция должна быть по своей сути безопасной — не только в том случае, если вы соблюдаете определенные правила. - person ; 12.10.2016
comment
@Bergi Спецификация просто требует, чтобы они не ломались - вот что я имею в виду под поощрением. - person ; 12.10.2016
comment
@evolutionxbox извините за задержку: thunk — это нулевая функция (без аргументов), единственной целью которой является ленивая оценка ее содержащие вычисления. - person ; 12.10.2016
comment
@ftor ты никогда не сможешь спасти людей, которые просто ничего не знают. while(true) может поместить вашу программу в бесконечный цикл - иногда намеренно, иногда нет - в любом случае, здесь есть некоторый риск, но я не думаю, что языки должны пытаться предотвратить использование людьми таких выражений. Мутации только наивно воспринимаются как всегда плохое — вызов итератора и локализация мутаций — вполне приемлемое/уместное использование интерфейса; и это не мешает вам писать функциональные, свободные от побочных эффектов программы. - person Mulan; 12.10.2016
comment
@naomik: ref.next() - это изменение состояния, и, на мой взгляд, это поведение довольно неявно. Этот пост может быть немного провокационным по тону, но только для того, чтобы подчеркнуть свою точку зрения. Я провел много времени с этим протоколом только для того, чтобы, наконец, понять, что я могу добиться аналогичного поведения, используя чистые функции, рекурсию и преобразователи, но без недостатков. - person ; 12.10.2016
comment
@naomik Работа с протоколом итерации просто увеличивает потенциал и риск побочных эффектов. Вам нужна большая доля уверенности в коде. Риск и польза языковых конструкций должны быть сбалансированы. Я думаю, что итераторы не соответствуют этому требованию. - person ; 12.10.2016
comment
@ftor уже слишком поздно говорить, что я полностью изменил свою позицию в отношении итераторов без гражданства с момента моего последнего комментария? Я совершенно забыл об этом вопросе, пока совсем недавно не работал над чем-то подобным (как вы видели) ^_^ - person Mulan; 16.09.2017
comment
@naomik, конечно нет, я всегда так делаю. Однако есть закономерность, которую я узнал: в конце концов я достиг этого с помощью какой-то функции, в основном комбинаторов, в основном чистых. - person ; 16.09.2017

Чистый итератор очень прост. Все, что нам нужно это

  • текущее значение
  • замыкание, продвигающее итератор
  • способ сигнализировать, что итератор исчерпан
  • соответствующую структуру данных, содержащую эти свойства

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 является универсальным, то есть работает с итераторами любого типа данных.

Может ли кто-нибудь объяснить мне, почему нативные итераторы имеют состояние? Почему разработчик языка ненавидит функциональное программирование?

person Community    schedule 13.10.2016