Чи дійсні функції генератора у функціональному програмуванні?


17

Питання:

  • Чи порушують генератори парадигму функціонального програмування? Чому або чому ні?
  • Якщо так, то чи можна використовувати генератори у функціональному програмуванні?

Розглянемо наступне:

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()чотирьох разів на об'єкт генератора також завжди повертав би четвертий елемент.

Для більш широкого контексту ці питання виникали під час роботи над програмою ката . Людина, яка відповіла на питання, поставила питання про те, чи можна використовувати генератори у функціональному програмуванні і чи є вони чи ні.


2
Кожна програма - державна. Справжнє питання полягає в тому, чи він кваліфікується як функціональний стан, який я трактую як "незмінний стан", стан, який не змінюється, як тільки йому призначено. Я стверджую, що єдиний спосіб, коли ви можете змусити генератор повертати щось інше під час кожного дзвінка, це якщо стан, який змінюється, якось задіяний.
Роберт Харві

Відповіді:


14

Функції генератора не особливо особливі. Ми можемо реалізувати подібний механізм самостійно, переписавши функцію генератора в стилі на основі зворотного виклику:

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 буде оцінюватися незалежно від того, де це відбувається і як часто ми повторюємо це, з генератором JS це не так - тоді , в один момент , це може бути і будь-яке число. Це проблематично, якщо ми передамо посилання на генератор іншій функції:252625

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.