Wie funktioniert die ST-Monade?

75

Ich verstehe, dass die ST-Monade so etwas wie ein kleiner Bruder von IO ist, der wiederum die Staatsmonade mit zusätzlicher RealWorldMagie ist. Ich kann Staaten Bild und ich kann vorstellen , dass Real World irgendwie in IO gesetzt wird, aber jedes Mal , wenn ich eine Art Unterschrift schreiben STdie sdes ST Monade verwirrt mich.

Nehmen Sie zum Beispiel ST s (STArray s a b). Wie funktioniert das sdort? Wird es nur verwendet, um eine künstliche Datenabhängigkeit zwischen Berechnungen aufzubauen, ohne wie Zustände in der Zustandsmonade (aufgrund der forall) referenziert werden zu können ?

Ich werfe nur Ideen aus und würde es wirklich begrüßen, wenn jemand besser informiert wäre als ich, um es mir zu erklären.

David
quelle
Wenn Sie nach "ST-Monade" suchen, gibt es einen Eintrag, der meine Frage als Nebenantwort auf eine Frage zu STArrays beantwortet. Ich bin mir nicht sicher, ob meine Frage hier jetzt ein Duplikat ist. stackoverflow.com/questions/8197032/…
David
2
Es ist so verlockend, einfach mit "Ja" zu antworten. auf deine Frage. :)
AndrewC
Ich denke, das Hinzufügen und Schließen eines Links zu der Frage würde a) die Suche in Zukunft erleichtern und b) einigen Antwortenden Zeit sparen.
David
2
Ich werde Sie auf meine Beschreibung von Rang-2-Typen hinweisen , die Ihnen auch helfen sollten, die STMonade zu verstehen . Sobald Sie verstanden haben, wie Rang-2-Typen steuern können, wer den für eine Typvariable verwendeten Typ auswählt, sollten Sie verstehen, wie STOperationen Rang-2 verwenden, um zu verhindern, dass ihre Berechnungen illegal verwendet werden.
Luis Casillas
Sie können schreiben, dass a :: ST Int Int; a = return 2 STes sich um eine ganz normale Zustandsmonade handelt, außer dass ein unboxed Paar als Ausgabe der Zustandsfunktion verwendet wird State# s -> (# State# s, a #), was es unpraktisch macht, damit umzugehen. Das Geheimnis liegt ganz darin, runSTdass es einen Typ vom Rang 2 hat, obwohl ST selbst keiner ist.
Antrag

Antworten:

74

Das verhindert, dass sGegenstände innerhalb der STMonade nach außen aus der STMonade austreten .

-- This is an error... but let's pretend for a moment...
let a = runST $ newSTRef (15 :: Int)
    b = runST $ writeSTRef a 20
    c = runST $ readSTRef a
in b `seq` c

Okay, dies ist ein Typfehler (was gut ist! Wir wollen nicht STRefaußerhalb der ursprünglichen Berechnung auslaufen!). Es ist ein Typfehler wegen des Extra s. Denken Sie daran, dass runSTdie Signatur hat:

runST :: (forall s . ST s a) -> a

Dies bedeutet, dass sfür die von Ihnen ausgeführte Berechnung keine Einschränkungen gelten müssen. Wenn Sie also versuchen zu bewerten a:

a = runST (newSTRef (15 :: Int) :: forall s. ST s (STRef s Int))

Das Ergebnis hätte einen Typ STRef s Int, was falsch ist, da der saußerhalb des forallIn "" entkommen "ist runST. Typvariablen müssen immer auf der Innenseite von a erscheinen forall, und Haskell erlaubt forallüberall implizite Quantifizierer. Es gibt einfach keine Regel, mit der Sie den Rückgabetyp von sinnvoll herausfinden können a.

Ein weiteres Beispiel mit forall: Um klar zu zeigen, warum Sie nicht zulassen können, dass Dinge einem entkommen forall, hier ein einfacheres Beispiel:

f :: (forall a. [a] -> b) -> Bool -> b
f g flag =
  if flag
  then g "abcd"
  else g [1,2]

> :t f length
f length :: Bool -> Int

> :t f id
-- error --

Dies ist natürlich f idein Fehler, da er entweder eine Liste Charoder eine Liste von zurückgeben würde, Intje nachdem, ob der Boolesche Wert wahr oder falsch ist. Es ist einfach falsch, genau wie das Beispiel mit ST.

Auf der anderen Seite, wenn Sie den sTypparameter nicht hätten , würde alles gut funktionieren , obwohl der Code offensichtlich ziemlich falsch ist.

Wie ST tatsächlich funktioniert: In Bezug auf die Implementierung ist die STMonade tatsächlich dieselbe wie die IOMonade, jedoch mit einer etwas anderen Schnittstelle. Wenn Sie die STMonade verwenden, erhalten Sie tatsächlich unsafePerformIOoder das Äquivalent hinter den Kulissen. Der Grund, warum Sie dies sicher tun können, liegt in der STTypensignatur aller verwandten Funktionen, insbesondere des Teils mit dem forall.

Dietrich Epp
quelle
Könnten Sie die Sicherheit von ST "brechen", indem Sie sie realWorld#direkt im Client-Code verwenden, um sie nachzuahmen runSTRep? Es ist natürlich eine schlechte Idee, aber ich frage mich, was der Schutz ist, abgesehen davon, realworld#dass er ein offensichtlich unsicherer Wert ist, der in einem Programm verwendet werden kann. hackage.haskell.org/package/base-4.6.0.1/docs/src/…
Misterbee
@misterbee Yep , und es ist nicht einmal unbedingt unsicher. Denken Sie daran, sexistiert nur auf Typebene.
PyRulez
28

Dies sist nur ein Hack, der das Typsystem davon abhält, Dinge zu tun, die unsicher wären. Zur Laufzeit "macht" es nichts. Der Typprüfer lehnt lediglich Programme ab, die zweifelhafte Dinge tun. (Es handelt sich um einen sogenannten Phantomtyp , der nur im Kopf des Typprüfers vorhanden ist und zur Laufzeit nichts beeinflusst.)

MathematicalOrchid
quelle