Sind Generatorfunktionen in der Funktionsprogrammierung gültig?

17

Die Fragen sind:

  • Brechen Generatoren das Paradigma der funktionalen Programmierung? Warum oder warum nicht?
  • Wenn ja, können Generatoren in der funktionalen Programmierung verwendet werden und wie?

Folgendes berücksichtigen:

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

Die downCounterMethode scheint zustandslos zu sein. Wenn Sie downCountermit der gleichen Eingabe aufrufen , erhalten Sie immer die gleiche Ausgabe. Gleichzeitig führt der Aufruf next()jedoch nicht zu konsistenten Ergebnissen.

Ich bin nicht sicher, ob Generatoren das funktionale Programmierparadigma brechen oder nicht, da es sich in diesem Beispiel counterum ein Generatorobjekt handelt und ein Aufruf daher next()zu denselben Ergebnissen führen würde wie ein anderes Generatorobjekt, das mit denselben Ergebnissen erstellt wurde maxValue.

Wenn Sie someCollection[3]ein Array aufrufen, wird immer das vierte Element zurückgegeben. In ähnlicher Weise next()würde ein viermaliger Aufruf eines Generatorobjekts auch immer das vierte Element zurückgeben.

Für mehr Kontext wurden diese Fragen während der Arbeit an einer Programmier-Kata aufgeworfen . Die Person, die die Frage beantwortet hat, hat die Frage aufgeworfen, ob Generatoren für die funktionale Programmierung verwendet werden können und ob sie den Status haben oder nicht.

Pete
quelle
2
Jedes Programm hält Zustand. Die eigentliche Frage ist, ob es sich um einen Funktionszustand handelt , den ich als "unveränderlichen Zustand" interpretiere, der sich nach seiner Zuweisung nicht ändert. Ich behaupte, dass der einzige Weg, wie Sie einen Generator dazu bringen können, bei jedem Aufruf etwas anderes zurückzugeben, darin besteht, dass ein veränderlicher Zustand in irgendeiner Weise involviert ist.
Robert Harvey

Antworten:

14

Generatorfunktionen sind nicht besonders speziell. Wir können einen ähnlichen Mechanismus selbst implementieren, indem wir die Generatorfunktion in einem Callback-basierten Stil umschreiben:

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

Klar, das downCounterist so pur und funktional wie es nur geht. Hier gibt es kein Problem.

Das von JavaScript verwendete Generatorprotokoll enthält ein veränderbares Objekt. Dies ist nicht erforderlich, siehe obigen Code. Insbesondere bedeuten veränderbare Objekte, dass wir die referenzielle Transparenz verlieren - die Fähigkeit, einen Ausdruck durch seinen Wert zu ersetzen. Während in meinem Beispiel counter.next().valuewird immer bewerten , 25egal wo sie auftritt und wie oft wir es wiederholen, ist dies nicht der Fall mit dem Generator JS - an einem Punkt ist es 26, dann 25, und es könnte wirklich ein beliebige Zahl sein. Dies ist problematisch, wenn wir einen Verweis auf den Generator an eine andere Funktion übergeben:

counter.next().value; //=> 25
otherFunction(counter); // does this consume the counter?
counter.next().value; // what will this be? It depends on the otherFunction()

Generatoren halten also eindeutig den Zustand und sind daher für eine „reine“ Funktionsprogrammierung ungeeignet. Glücklicherweise müssen Sie keine reine Funktionsprogrammierung durchführen und können stattdessen pragmatisch sein. Wenn Generatoren Ihren Code klarer machen, sollten Sie sie ohne schlechtes Gewissen verwenden. Schließlich ist JavaScript keine reine Funktionssprache, im Gegensatz zu z. B. Haskell.

Übrigens gibt es in Haskell keinen Unterschied zwischen der Rückgabe einer Liste und eines Generators, da hier eine verzögerte Auswertung verwendet wird:

downCounter :: Int -> [Int]
downCounter maxValue =
  maxValue : (downCounter (max 0 (maxValue - 1)))
-- invoke as "take n (downCounter 26)" to display n elements
amon
quelle