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
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