2017-06-08 1 views
0

J'essaie d'utiliser des tuyaux pour modéliser un problème pour lequel les instances bidirectionnelles Proxy seraient idéales. Au fond, j'ai quelque chose comme l'architecture suivante:Comment utiliser correctement les tuyaux bidirectionnels?

api logic 
    | ^
    | | 
    v | 
    A A' 
layer 1 
    B B' 
    | ^
    | | 
    v | 
layer 2  

donc en gros, j'ai layer 1 qui est un transformateur bidirectionnel. Le modèle est basé sur le pull, donc les flux de messages sont déclenchés par des pulls depuis le composant logic.

Je devrais avoir layer1 :: Proxy A' A B' B m x, l'idée étant que layer1 tire A de api, fait une transformation A -> B utilise alors la B' de layer2, applique B' -> A' et le transmettre à la logic.

Ce qui est pas clair: je sais comment request un A et répondre d'une B, mais comment puis-je produire le A' du B'? Il ne semble pas Combinator dans la bibliothèque qui correspond ici ...

Répondre

2

Il existe trois types que vous devez être au courant: Client s peuvent request mais jamais respond, Server s peuvent respond mais jamais request et Proxy peuvent faire tous les deux.

L'argument à request/respond est la valeur à envoyer, et le résultat que vous liez est la réponse/requête, respectivement. Cela a un sens intuitif pour request (vous liez la réponse), mais il a fallu un peu de temps avant que je l'obtienne pour cliquer sur respond (vous liez la requête suivante). Il rend vos étapes de traitement propres à de petites fonctions récursives. (Mon instinct initial était d'utiliser Control.Monad.forever, ce qui fonctionne bien pour les tuyaux unidirectionnels mais est le mauvais outil ici.)

Le bit qui devient confus: parce que les tuyaux eux-mêmes sont synchrones, vous devez obtenir une valeur initiale pour circuler et coup de pied les choses. Soit vous le transmettez en request (en créant un pipeline de tirage que vous composez avec (>~>)) soit vous le transmettez en respond (créant un pipeline de push que vous composez avec (>+>)). Ensuite, vous passez la valeur initiale dans le pipeline composé, vous donnant le Effect m r qui peut aller à runEffect.

J'ai utilisé un pipeline d'extraction dans l'exemple ci-dessous, car il correspond à votre métaphore de demande d'API. Il met en œuvre ce pipeline bidirectionnel en trois étapes:

+--------+ Yoken +-----------+ Token +--------+ 
|  |<-------|   |<-------|  | 
| server |  | transform |  | client | 
|  |------->|   |------->|  | 
+--------+ String +-----------+ String +--------+ 
           (shouty) 

client génère demande Token s et imprime des réponses. transform transforme les Token en Yoken s (hé, les touches sont l'une à côté de l'autre) et les transmet en amont. Il transforme également la réponse en un cri en mettant en évidence et en ajoutant un !. server reçoit Yoken s et génère le numéro demandé de yo s.

import Data.Char 
import Control.Monad 
import Control.Monad.IO.Class 
import Pipes.Core 
import System.IO 

data Token = Token Int 
data Yoken = Yoken Int 

main :: IO() 
main = do 
    hSetBuffering stdout NoBuffering 
    -- We have to get an initial n outside the pipeline to kick things off. 
    putStr "N? " 
    n <- read <$> getLine 
    runEffect $ server >+> transform >+> client $ Token n 

-- The "server" generates a string of "yo"s based on the number inside the Yoken 
server :: Monad m => Yoken -> Server Yoken String m a 
server (Yoken n) = (respond . concat $ replicate n "yo") >>= server 

-- A processing step just for the sake of having one, turn the Token into a 
-- Yoken, upcase the string, and append a "!". 
transform :: Monad m => Token -> Proxy Yoken String Token String m a 
transform (Token t) = do 
    resp <- request $ Yoken t 
    next <- respond $ map toUpper resp ++ "!" 
    transform next 

-- Clients request "yo"s, by sending `Token`s upstream. 
client :: Token -> Client Token String IO a 
client t = do 
    resp <- request t 
    n <- liftIO $ putStrLn resp *> putStr "N? " *> fmap read getLine 
    client $ Token n 
+0

Merci beaucoup d'avoir pris soin de répondre à ma question solitaire! J'ai réussi à trouver la réponse moi-même grâce à http://hackage.haskell.org/package/pipes-2.3.0/docs/Control-Proxy-Tutorial.html – insitu