2012-12-17 4 views
19

Es scheint mir, dass es eine starke Verbindung zwischen den beiden Ideen gibt. Meine Vermutung ist, dass FRP in Bezug auf Iteratees implementiert werden könnte, wenn es eine Möglichkeit geben würde, beliebige Graphen mit Iteraten auszudrücken. Aber afaik unterstützen sie nur kettenartige Strukturen.Was ist die Verbindung zwischen Iteraten und FRP?

Kann jemand etwas Licht darauf werfen?

Antwort

13

Es ist anders herum. Es besteht eine starke Verbindung zwischen AFRP und Stream-Verarbeitung. In der Tat AFRP ist eine Form von Stream-Verarbeitung, und Sie das Idiom etwas sehr ähnlich Rohre können implementieren:

data Pipe m a b = 
    Pipe { 
     cleanup :: m(), 
     feed :: [a] -> m (Maybe [b], Pipe m a b) 
    } 

Das ist eine Erweiterung der Draht Kategorien wie in NetWire gefunden. Es empfängt den nächsten Teil der Eingabe und gibt Nothing zurück, wenn es nicht mehr produziert. Diese Datei Leser hätte den folgenden Typ:

readFile :: (MonadIO m) => FilePath -> Pipe m a ByteString 

Pipe ist eine Familie von applicative functors, so eine einfache Funktion der Stromelemente anzuwenden, die Sie gerade fmap verwenden:

fmap (B.map toUpper) . readFile 

Für Ihre Bequemlichkeit ist es auch eine Familie von profunctors.

Das interessanteste Merkmal ist, dass dies eine Familie von alternativen Funktoren ist. Damit können Sie Streams routen und mehreren Stream-Prozessoren erlauben, sie zu "versuchen", bevor Sie aufgeben. Dies kann zu einer vollwertigen Parsing-Bibliothek erweitert werden, die sogar einige statische Informationen für Optimierungszwecke verwenden kann.

+0

Ah, ich wollte etwas Ähnliches posten, aber ich werde auf die Expertise von jemandem verzichten, der tatsächlich eine AFRP-Bibliothek geschrieben hat. :] –

+0

Es scheint, dass die Verwendung von (A) FRP I nicht auf eine azyklische Graphenstruktur beschränkt wäre, ist das wahr? – fho

+3

Es ist mir aus Ihrer Antwort nicht klar, was ist, wenn irgendwelche Aspekte von AFRP nicht in Begriffen oder dem kürzlichen Strom implementiert werden können, der Bibliotheksrohre/-rohre besitzt? – Davorak

13

Sie können eine eingeschränkte Form von FRP mit Stream-Prozessoren implementieren. Um zum Beispiel der pipes Bibliothek verwenden, können Sie eine Quelle der Ereignisse definieren:

mouseCoordinates :: (Proxy p) =>() -> Producer p MouseCoord IO r 

... und Sie könnten in ähnliche Weise einen grafischen Handler definieren, die Mauskoordinaten und aktualisiert einen Cursor auf einer Leinwand nimmt:

coordHandler :: (Proxy p) =>() -> Consumer p MouseCoord IO r 

Dann würden Sie die Mausereignisse an den Handler Zusammensetzung Haken:

>>> runProxy $ mouseCoordinates >-> coordHandler 

Und es wäre nur wie erwartet laufen.

Wie Sie sagten, funktioniert dies gut für eine einzelne Kette von Stufen, aber was ist mit beliebigeren Topologien? Nun, es stellt sich heraus, dass, da der zentrale Proxy Typ von pipes ein Monad Transformer ist, Sie jede beliebige Topologie modellieren können, indem Sie einfach Proxy-Monade-Transformer auf sie selbst verschachteln. Zum Beispiel würden Sie hier zwei Eingabeströme zippen:

zipD 
:: (Monad m, Proxy p1, Proxy p2, Proxy p3) 
=>() -> Consumer p1 a (Consumer p2 b (Producer p3 (a, b) m)) r 
zipD() = runIdentityP $ hoist (runIdentityP . hoist runIdentityP) $ forever $ do 
    a <- request()    -- Request from the outer Consumer 
    b <- lift $ request()  -- Request from the inner consumer 
    lift $ lift $ respond (a, b) -- Respond to the Producer 

Dies verhält sich wie eine Curry-Funktion. Sie wenden es teilweise nacheinander auf jeden Eingang an, und Sie können es dann ausführen, wenn es vollständig angewendet wird.

-- 1st application 
p1 = runProxyK $ zipD <-< fromListS [1..] 

-- 2nd application 
p2 = runProxyK $ p2  <-< fromListS [4..6] 

-- 3rd application 
p3 = runProxy $ printD <-< p3 

Es läuft so, wie man erwarten: verallgemeinert

>>> p3 
(1, 4) 
(2, 5) 
(3, 6) 

Dieser Trick zu jeder Topologie.Sie können viel mehr Details darüber in Control.Proxy.Tutorial im Abschnitt "Zweige, Reißverschlüsse und Zusammenführungen" finden. Insbesondere sollten Sie sich den fork Kombinator ansehen, den Sie als Beispiel verwenden, mit dem Sie einen Stream in zwei Ausgaben aufteilen können.

+0

Ich bin mir ziemlich sicher, dass dies für alle gesunden iteratee Bibliotheken gilt. Oleg hat es vor einiger Zeit benutzt. Ich bin mir nicht sicher, warum sich jemals jemand daran erinnert, dass es möglich ist; Die Technik ist sehr nützlich. –

+0

@JohnL Deshalb musst du es predigen! Nicht jeder weiß, was Oleg getan hat. –

+0

Selbst Leute, die wissen, was Oleg getan hat, verstehen es oft nicht. Leider bin ich oft in dieser Gruppe, zumindest auf den ersten ... –

Verwandte Themen