2013-06-20 9 views
6

Sagen wir, ich habe diesen Code:Wie kann ich eine Leitung mit mehreren Kommunikationsarten haben?

import Control.Monad.State hiding (StateT) 
import Control.Proxy 

server :: (Proxy p, Monad m) => Int -> Server p Int Bool (StateT Int m)() 
server = runIdentityK loop 
    where loop arg = do 
     currMax <- lift get 
     lift $ put $ max currMax arg 
     nextArg <- respond (even arg) 
     loop nextArg 

client :: (Proxy p, Monad m) => Client p Int Bool m() 
client = runIdentityP loop 
    where loop = go 1 
      go i = do 
      isEven <- request i 
      go $ if isEven 
       then i `div` 2 
       else i * 3 + 1 

Derzeit immer der Client sendet Int und empfängt Bool. Ich möchte jedoch, dass der Client auch den höchsten Wert abfragen kann, den der Server bisher gesehen hat. Also brauche ich auch die Kommunikation von Senden () und Empfangen Int. Ich konnte dies als der Kunde kodieren, der Either Int() sendet und Either Bool Int empfängt. Allerdings möchte ich sicherstellen, dass die beiden nicht gemischt sind - Senden einer Int erhält immer eine Bool Antwort.

Wie kann das gemacht werden?

+0

Warum würde nicht funktionieren? Wenn Sie ein Entweder an den Server senden, können Sie in den Fällen Links oder Rechts Muster zuordnen und eine entsprechende Links- oder Rechtsrückseite senden. – Dwilson

+3

@Dwilson: Er möchte statisch sicherstellen, dass das Senden von 'Int' immer ein' Bool' zurückgibt. Die Verwendung von "Entweder" würde diese Prüfung zur Laufzeit verschieben. –

Antwort

5

Wenn Sie möchten, dass eine Pipeline zwei separate Schnittstellen hat, müssen Sie den Proxy Monaden-Transformator in sich verschachteln. Das bedeutet, dass Sie die Art wollen:

twoInterfaces 
    :: (Monad m, Proxy p1, Proxy p2) 
    =>() -> Int -> Server p1 Int Bool (Server p2() Int m) r 
twoInterfaces() n = runIdentityP . hoist runIdentityP $ do 
    x <- respond A  -- Use outer interface 
    y <- lift $ respond B -- Use inner interface 
    ... 

die folgenden zwei Clients für jede Schnittstelle Gegeben:

client1 :: (Monad m, Proxy p) =>() -> Client p Int Bool m r 
client2 :: (Monad m, Proxy p) =>() -> Client p() Int m r 

Sie würden sie auf die beiden Server-Schnittstellen anschließen:

oneInterface() = runProxy (twoInterfaces() >-> client1) 

main = runProxy (client1 >-> oneInterface) 

Um zu erfahren, Weitere Informationen zu diesem Trick finden Sie im Abschnitt Branching, zips, and merges des aktuellen Lernprogramms.

Sie können es auch anders herum tun. Sie können einen Client mit zwei getrennten Schnittstellen haben und zwei verschiedene Server s anschließen. Dies kann oder kann nicht zu Ihrem Problem besser passen.

Beachten Sie, dass dies in pipes-4.0.0 viel einfacher erhalten wird (zur Zeit auf Github), wo die Typen viel prägnanter sein wird, und Sie werden runIdentityP nicht brauchen:

twoInterfaces 
    :: (Monad m) =>() -> Int -> Server Int Bool (Server() Int m) r 
twoInterface() n = do 
    x <- respond A 
    y <- lift $ respond B 
    ... 

client1 :: (Monad m) =>() -> Client Int Bool m r 
client2 :: (Monad m) =>() -> Client() Int m r 
+0

Ich musste den zweiten (Pipes 4.0.0) Snippet ein wenig zum Kompilieren ändern, indem ich den inneren 'Server() Int m' zu' Proxy xy() Int m' erweitere, weil (zu meinem Verständnis) es keinen Weg gibt Haben Sie einen teilweise angewandten Typ Synonym mit GHC – ajp

+1

@ajp Sie können auch 'Server' verwenden, um es auch zu arbeiten. Jedes Synonym für einen polymorphen Typ hat ein konkretes, das nicht vollständig auf alle Typparameter angewendet werden muss. –

1

Sie können zwei Pipelines verwenden, die auf Ihre beiden Anwendungsfälle zugeschnitten sind. Ein Server würde Bool zurückgeben, der andere würde Int zurückgeben. Ein Client würde einen Bool akzeptieren, der andere würde einen Int akzeptieren. Die beiden Clients wären eigentlich Pipe s. Man würde einen Left Int zurückgeben, der andere würde einen Right Bool zurückgeben (oder umgekehrt). Die zwei Server könnten in einer IORef oder etwas ähnlichem übergeben werden. Sie können dies verwenden, um den Maximalwert zu verfolgen. A Pipe unter Either Int Bool am Ende der beiden Clients könnte verwendet werden, um etwas mit den beiden Fällen Left und Right Fällen von den Clients zurückgegeben werden.

Verwandte Themen