2015-02-27 3 views
11

Was können die Unterschiede und die beabsichtigten Anwendungen für ioToST und unsafeSTToIO sein, die in GHC.IO definiert sind?Was ist der Unterschied zwischen `ioToST` und` unsafeIOToST` aus GHC.IO

-- --------------------------------------------------------------------------- 

-- Coercions between IO and ST 

-- | A monad transformer embedding strict state transformers in the 'IO' 
-- monad. The 'RealWorld' parameter indicates that the internal state 
-- used by the 'ST' computation is a special one supplied by the 'IO' 
-- monad, and thus distinct from those used by invocations of 'runST'. 
stToIO  :: ST RealWorld a -> IO a 
stToIO (ST m) = IO m 

ioToST  :: IO a -> ST RealWorld a 
ioToST (IO m) = (ST m) 

-- This relies on IO and ST having the same representation modulo the 
-- constraint on the type of the state 
-- 
unsafeIOToST  :: IO a -> ST s a 
unsafeIOToST (IO io) = ST $ \ s -> (unsafeCoerce# io) s 

unsafeSTToIO :: ST s a -> IO a 
unsafeSTToIO (ST m) = IO (unsafeCoerce# m) 
+5

Der Unterschied liegt in den Typen.ioToST kann nur eine 'ST RealWorld a' erzeugen, die nicht an' runST :: (forall s. ST s a) -> a' übergeben werden kann. – user2407038

Antwort

12

Die sicheren Versionen müssen im IO-Monade starten (weil Sie keine ST RealWorld von runST erhalten) können Sie zwischen dem IO-Kontext und einem ST RealWorld Kontext wechseln. Sie sind sicher, weil ST RealWorld im Grunde die gleiche Sache wie IO ist.

Die unsicheren Versionen können überall gestartet werden (weil runST überall angerufen werden kann) und ermöglichen es Ihnen, zwischen einer beliebigen ST-Monade und der IO-Monade zu wechseln. Die Verwendung von runST aus einem reinen Kontext und dann eine unsafeIOToST innerhalb der State-Monade zu tun ist im Grunde äquivalent zur Verwendung von unsafePerformIO.

+4

Ich weiß, dass es nicht die Frage des OP ist, aber in meinen Augen ist die größere Frage, was, wenn überhaupt, 'unsafeSTToIO' unsicher macht. – dfeuer

+1

@dfeuer Es könnte entweder unsicher nur für Symmetrie mit 'unsafeIOToST' genannt werden, oder vielleicht können Sie einen Weg finden, um es zu verwenden, um eine' STVar' außerhalb seiner 'runST'-Wrapper zugreifen, in diesem Fall wäre es wirklich unsicher alle durch selbst. –

+2

Ich habe einen Thread auf Haskell-Cafe darüber gefunden. Es scheint, dass es wirklich unsicher ist, aber ich verstehe keines der Beispiele. – dfeuer

9

TL; DR. Alle vier dieser Funktionen sind nur Typumwandlungen. Sie sind alle zur Laufzeit nicht betriebsbereit. Die nur Unterschied zwischen ihnen ist die Art Signaturen —, aber es ist die Art Signaturen, die alle Sicherheitsgarantien an erster Stelle erzwingen!


Die ST Monade und die IO Monade beide geben Ihnen wandelbar Zustand.

Es ist bekanntlich unmöglich, die IO Monade zu entkommen. [Nun, nein, Sie können, wenn Sie unsafePerformIO verwenden. Tun Sie das nicht!] Aus diesem Grund wird die gesamte E/A, die Ihr Programm jemals ausführt, in einem einzigen riesigen IO Block gebündelt, wodurch eine globale Reihenfolge der Operationen erzwungen wird. [Zumindest bis Sie forkIO nennen, aber trotzdem ...]

Der Grund unsafePerformIO so unsicher ist verdammt ist, dass es keine Möglichkeit, wann genau herauszufinden, ob oder wie oft eingeschlossen I/O Operationen passieren —, die in der Regel eine sehr schlechte Sache ist.

Die ST Monade stellt auch wandelbar Zustand, aber es tut einen Fluchtmechanismus haben — die runST Funktion. So können Sie aus einem unreinen Wert einen reinen Wert machen. Aber jetzt gibt es keine Möglichkeit zu garantieren, welche Reihenfolge separate ST Blöcke laufen wird. Um vollständige Zerstörung zu verhindern, müssen wir sicherstellen, dass separate ST Blöcke nicht miteinander "stören" können.

Aus diesem Grund können Sie in der ST Monad keine E/A-Operationen ausführen. Sie können auf den veränderbaren Status zugreifen, aber dieser Status darf nicht aus dem Block ST entfernt werden.

Die IO Monade und die ST Monade sind eigentlich das gleiche Monade. Und ein IORef ist eigentlich ein STRef, und so weiter. Es wäre also wirklich nützlich, Code schreiben und in beiden Monaden verwenden zu können. Und alle vier von Ihnen genannten Funktionen sind Typumwandlungen, mit denen Sie genau das tun können.

Um die Gefahr zu verstehen, müssen wir verstehen, wie ST seinen kleinen Trick erreicht. Es ist alles im Phantom s geben Sie die Typ-Signaturen ein. Um einen ST Block laufen, muss es für alle möglichen s arbeiten:

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

das All wandelbar Zeug hat s in der Art als auch, und durch einen glücklichen Zufall, das bedeutet, dass jeder Versuch, wandelbar Sachen zurückgeführt der ST Monade wird schlecht typisiert sein. (Dies ist wirklich ein bisschen ein Hack, aber es funktioniert so perfekt ...)

Zumindest wird es schlecht getippt werden, wenn Sie runST verwenden. Beachten Sie, dass ioToST Ihnen eine ST RealWorld x gibt. Grob gesagt, IO x & ca. ST RealWorld x. Aber runST wird das nicht als Eingabe akzeptieren. Sie können runST nicht verwenden, um I/O auszuführen.

Die ioToST gibt Ihnen einen Typ, den Sie nicht mit runST verwenden können. Aber unsafeIOToST gibt Ihnen einen Typ, der gut mit runST funktioniert. An diesem Punkt haben Sie im Grunde implementiert unsafePerformIO:

unsafePerformIO = runST . ioToST 

Die unsafeSTToIO ermöglicht es Ihnen, wandelbar Sachen aus einem ST Block und möglicherweise in eine andere zu bekommen:

foobar = do 
    v <- unsafeSTToIO (newSTRef 42) 
    let w = runST (readSTRef v) 
    let x = runST (writeSTRef v 99) 
    print w 

Wanna nehmen ein erraten, was los ist gedruckt werden? Weil die Sache ist, haben wir drei ST Aktionen hier, die in absolut beliebiger Reihenfolge passieren können. Wird die readSTRef vor oder nach der writeSTRef passieren?

[Tatsächlich, in diesem Beispiel, geschieht der Schreibvorgang nie, weil wir nichts mit x "tun". Aber wenn ich x zu einem entfernten, nicht verwandten Teil des Codes übergebe, und dieser Code passiert, um es zu inspizieren, macht unsere E/A-Operation plötzlich etwas anderes. Reiner Code sollte nicht in der Lage sein, so wandelbar Sachen beeinflussen]


bearbeiten: Es scheint, ich war etwas verfrüht. Die unsafeSTToIO Funktion ermöglicht es Ihnen, einen veränderbaren Wert aus der ST Monade zu nehmen, aber es scheint, es erfordert einen zweiten Anruf an unsafeSTToIO, um die veränderbare Sache wieder in die die ST Monad setzen. (An diesem Punkt beide Aktionen sind IO Aktionen, so dass ihre Bestellung garantiert.)

Sie könnten natürlich Mix in einigen unsafeIOToST als gut, aber das beweist nicht wirklich, dass unsafeSTToIO selbst unsicher ist:

foobar = do 
    v <- unsafeSTToIO (newSTRef 42) 
    let w = runST (unsafeIOToST $ unsafeSTToIO $ readSTRef v) 
    let x = runST (unsafeIOToST $ unsafeSTToIO $ writeSTRef v 99) 
    print w 

Ich habe damit herum gespielt, und ich habe es noch nicht geschafft, die Typüberprüfer zu überzeugen, etwas Provokativ unsicher zu machen mit nurunsafeSTToIO. Ich bin davon überzeugt, dass dies möglich ist, und die verschiedenen Kommentare zu dieser Frage scheinen zu stimmen, aber ich kann kein Beispiel konstruieren. Sie bekommen die Idee zwar; ändere die Typen und deine Sicherheit wird gebrochen.

+3

"Es wäre also wirklich nützlich, Code schreiben und in beiden Monaden verwenden zu können." Es gibt tatsächlich einen sehr sauberen Weg, um diesen Zweck ohne etwas unsicheres zu erreichen: ['primitive'] (https://hackage.haskell.org/package/primitive) bietet eine' PrimMonad' Klasse mit 'ST s' und' IO' Instanzen und so ziemlich alles, was Sie wollen, um Funktionen zu schreiben, die mit beiden Instanzen umgehen können. Diese Konvertierungen werden nur benötigt, um mit der Tatsache umzugehen, dass viele Bibliotheksfunktionen (unnötig) spezifisch sind. – dfeuer

+1

Er ... Ich habe es gerade versucht, und Ihr Programm tippt nicht nach. Die realen Beispiele scheinen etwas subtiler zu sein. – dfeuer

+2

Ich glaube, das 'foobar' Beispiel kann nicht funktionieren. Der Grund dafür ist, dass 'runST' einen polytypischen Wert benötigt, während' v' monotypisiert ist. Tatsächlich hat "unsafeSTToIO (newSTRef 42)" den Typ 'forall s. IO (STRef s Int) ', und nicht' IO (forall s. STRef s Int) '. – chi

Verwandte Themen