Ein Funktions-Imperativ-Hybrid

8

Reine funktionale Programmiersprachen erlauben keine veränderlichen Daten, aber einige Berechnungen werden eher natürlich / intuitiv imperativ ausgedrückt - oder eine imperative Version eines Algorithmus kann effizienter sein. Ich bin mir bewusst, dass die meisten funktionalen Sprachen nicht rein sind und dass Sie Variablen zuweisen / neu zuweisen und imperative Dinge tun können, aber generell davon abraten.

Meine Frage ist: Warum darf der lokale Zustand nicht in lokalen Variablen manipuliert werden, sondern erfordert, dass Funktionen nur auf ihre eigenen lokalen Variablen und globalen Konstanten (oder nur in einem äußeren Bereich definierte Konstanten) zugreifen können? Auf diese Weise behalten alle Funktionen referenzielle Transparenz bei (sie geben immer denselben Rückgabewert bei denselben Argumenten an), aber innerhalb einer Funktion kann eine Berechnung imperativ ausgedrückt werden (wie zB eine while-Schleife).

IO und so könnte man immer noch auf normale funktionale Art und Weise erreichen - durch Monaden oder Umgehen eines "Welt" - oder "Universum" -Token.

    
Richard JP Le Guen 16.08.2011, 05:56
quelle

2 Antworten

3

Die kurze Antwort lautet: Es gibt Systeme, die erlauben, was Sie wollen. Zum Beispiel können Sie es mit der ST monad in Haskell tun (wie in den Kommentaren verwiesen).

Der Ansatz ST monad stammt von Haskells Control.Monad.ST . Code, der in der ST -Monade geschrieben ist, kann Referenzen ( STRef ) verwenden, wo es praktisch ist. Der nette Teil ist, dass Sie sogar die Ergebnisse von ST monad im reinen Code verwenden können, da es im Wesentlichen in sich abgeschlossen ist (das ist im Grunde das, was Sie in der Frage wollten).

Der Nachweis dieser in sich geschlossenen Eigenschaft erfolgt durch das Typsystem. Die ST -Monade enthält einen state-thread-Parameter, der normalerweise mit einer Typvariablen s angegeben wird. Wenn Sie eine solche Berechnung haben, haben Sie ein monadisches Ergebnis mit einem Typ wie:

%Vor%

Um dies zu einem reinen Ergebnis zu machen, müssen Sie

verwenden %Vor%

Sie können diesen Typ wie folgt lesen: Geben Sie mir eine Berechnung, bei der der Parameter s type keine Rolle spielt, und ich kann Ihnen das Ergebnis der Berechnung ohne das ST baggage zurückgeben. Dies verhindert grundsätzlich, dass die veränderbaren Variablen ST entweichen, da sie die s mit sich führen würden, die vom Typsystem erfasst würden.

Dies kann gut für reine Strukturen verwendet werden, die mit zugrunde liegenden veränderbaren Strukturen implementiert sind (wie das Vektorpaket <) / a>). Man kann die Unveränderlichkeit für eine begrenzte Zeit ablegen, um etwas zu tun, das das zugrundeliegende Array an Ort und Stelle verändert. Zum Beispiel könnte man das unveränderliche Vector mit einem Paket mit unreinen Algorithmen kombinieren, um es am besten zu behalten der Leistungsmerkmale der vor Ort Sortieralgorithmen und immer noch Reinheit erhalten.

In diesem Fall würde es etwa so aussehen:

%Vor%

Die Funktionen thaw und freeze sind lineares Kopieren, aber dies wird die gesamte O (n lg n) Laufzeit nicht stören. Sie können sogar unsafeFreeze verwenden, um einen weiteren linearen Durchlauf zu vermeiden, da der veränderbare Vektor nicht erneut verwendet wird.

    
ScottWest 05.07.2012, 11:43
quelle
3
  

Meine Frage ist, warum der lokale Zustand nicht in lokalen Variablen manipuliert werden darf, sondern dass Funktionen nur auf ihre eigenen lokalen und globalen Konstanten (oder nur in einem äußeren Bereich definierte Konstanten) zugreifen können?

Gute Frage. Ich denke, die Antwort ist, dass wandelbare Locals von begrenztem praktischen Wert sind, aber veränderliche Heap-zugeteilte Datenstrukturen (hauptsächlich Arrays) sind enorm wertvoll und bilden das Rückgrat vieler wichtiger Sammlungen, einschließlich effizienter Stapel, Warteschlangen, Mengen und Wörterbücher. Eine Beschränkung der Mutation auf Einheimische würde also keine ansonsten rein funktionale Sprache für einen der wichtigen Vorteile einer Mutation geben.

In einer verwandten Anmerkung bieten sequentielle Prozesse, die rein funktionale Datenstrukturen austauschen, viele Vorteile beider Welten, da die sequentiellen Prozesse die Mutation intern verwenden können, z. Veränderbare Nachrichtenwarteschlangen sind ~ 10x schneller als alle rein funktionalen Warteschlangen. Zum Beispiel ist dies idiomatisch in F #, wo der Code in MailboxProcessor veränderbare Datenstrukturen verwendet, aber die zwischen ihnen kommunizierten Nachrichten unveränderlich sind.

Das Sortieren ist eine gute Fallstudie in diesem Zusammenhang. Sedgewicks Quicksort in C ist kurz und einfach und hunderte Male schneller als die schnellste rein funktionale Art in jeder Sprache. Der Grund dafür ist, dass Quicksort das Array an Ort und Stelle mutiert. Veränderliche Einheimische würden nicht helfen. Die gleiche Geschichte für die meisten Graphalgorithmen.

    
Jon Harrop 05.07.2012 08:43
quelle