Hier ist was ich bisher habe:
%Vor% whileLoop
funktioniert gut, um for
loops zu unterstützen, aber ich sehe nicht, wie while loops unterstützt werden. Ein Teil des Problems ist, dass die Übersetzung von while-Schleifen delay
verwendet, was ich in diesem Fall nicht herausfinden konnte. Die offensichtliche Implementierung ist wahrscheinlich falsch, da sie die Berechnung nicht verzögert, sondern stattdessen ausführt!
Die Verzögerung verhindert auch try...with
und try...finally
.
Es gibt zwei verschiedene Möglichkeiten, Fortsetzungs-Builder in F # zu implementieren. Eine davon ist die Darstellung von verzögerten Berechnungen mit dem monadischen Typ (wenn eine Art der Darstellung von verzögerten Berechnungen unterstützt wird, wie Async<'T>
oder der unit -> option<'T>
Typ, wie durch kkm angezeigt.
Sie können jedoch auch die Flexibilität von F # -Berechnungsausdrücken verwenden und einen anderen Typ als Rückgabewert von Delay
verwenden. Dann müssen Sie die Operation Combine
entsprechend ändern und auch Run
member implementieren, aber alles funktioniert ganz gut:
Der Trick ist, dass der F # -Compiler Delay
verwendet, wenn Sie eine Berechnung haben, die verzögert werden muss - das heißt: 1) um die gesamte Berechnung zu umbrechen, 2) wenn Sie sequentiell Berechnungen erstellen, z. Verwenden von if
in der Berechnung und 3) zum Verzögern von Körpern von while
oder for
.
In der obigen Definition gibt das Delay
Mitglied unit -> M<'a>
statt M<'a>
zurück, aber das ist völlig in Ordnung, weil Combine
und While
als zweites Argument unit -> M<'a>
nehmen. Darüber hinaus wird durch das Hinzufügen von Run
, das die Funktion auswertet, das Ergebnis von maybe { .. }
block (eine verzögerte Funktion) ausgewertet, da der gesamte Block an Run
:
Dies ist eine Möglichkeit, den Berechnungsgenerator für nicht verzögerte Typen zu definieren, der höchstwahrscheinlich effizienter ist als der Wrapping-Typ innerhalb einer Funktion (wie in der kkm-Lösung) und keine spezielle verzögerte Version des Typs definieren muss.
Beachten Sie, dass dieses Problem z. Haskell, weil das eine faule Sprache ist, also müssen Berechnungen nicht explizit verzögert werden. Ich denke, dass die F # -Übersetzung sehr elegant ist, da sie sowohl verzögerte Typen ( Delay
, die M<'a>
zurückgeben) als auch Typen, die nur ein sofortiges Ergebnis darstellen, erlaubt (mit Delay
, das eine Funktion & amp;% zurückgibt) co_de%).
Laut monadischen Identitäten sollte Ihr delay
immer
Seit
%Vor% Die delay
hat die Signatur von
'T
ist typgebunden an unit
. Beachten Sie, dass die Argumente Ihrer bind
-Funktion von der üblichen Reihenfolge bind p rest
abweichen. Dies ist technisch gleich, aber komplizierter Lesecode.
Da Sie den monadischen Typ als type Maybe<'a> = option<'a>
definieren, gibt es keine Verzögerung für eine Berechnung, da der Typ überhaupt keine Berechnungen, sondern nur einen Wert umschließt. Also ist die Definition der Verzögerung als let delay f = f()
theoretisch korrekt. Für eine while-Schleife reicht es jedoch nicht aus: Der "Körper" der Schleife wird vor seiner "Testbedingung" berechnet, bevor die bind
tatsächlich gebunden ist. Um dies zu vermeiden, definieren Sie Ihre Monade mit einer zusätzlichen Verzögerungsebene neu: Anstatt einen Wert zu umbrechen, wickeln Sie eine Berechnung um, die eine Einheit benötigt und den Wert berechnet.
Beachten Sie, dass die umbrochene Berechnung nicht innerhalb der bind
-Funktion ausgeführt wird, d. e. nicht ausgeführt, bis die Argumente für bind
selbst gebunden sind.
Mit dem obigen Ausdruck wird delay
korrekt zu
Tags und Links f# computation-expression