zeromq4-patterns-0.3.0.0: src/System/ZMQ4/Patterns/RequestReply.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
module System.ZMQ4.Patterns.RequestReply (
-- * Type class
RequestReply
-- * Server and client
, responder
, request
) where
import Control.Monad (forever)
import Control.Monad.IO.Class (liftIO)
import Data.Binary
import qualified Data.ByteString.Lazy as BL
import System.ZMQ4.Monadic
-- | A request-reply type class.
--
-- @a@ is the request type, @b@ is the response type.
--
-- Example:
--
-- >>> {-# LANGUAGE DataKinds #-}
-- >>> {-# LANGUAGE TypeApplications #-}
-- >>>
-- >>> import Control.Concurrent.Async
-- >>> import Data.Binary
-- >>>
-- >>> data A = A deriving (Binary, Show)
-- >>> data B = B deriving (Binary, Show)
-- >>>
-- >>> instance RequestReply A B
-- >>>
-- >>> reply :: A -> IO B
-- >>> reply _ = return B
-- >>>
-- >>> main :: IO ()
-- >>> main = withAsync (responder "tcp://*:5000" reply) $ \_ ->
-- >>> requester "tcp://127.0.0.1:5000" A >>= print
--
class (Binary a, Binary b) => RequestReply a b | a -> b where
-- | Start responding using the given type class.
--
-- See 'RequestReply' for an example.
--
-- Silently ignores a request when decoding fails
responder :: forall a b .
RequestReply a b
=> String -- ^ Address to bind to
-> (a -> IO b) -- ^ Reply function
-> IO ()
responder addr reply = runZMQ $ do
rep <- socket Rep
bind rep addr
forever $ (decodeOrFail . BL.fromStrict <$> receive rep) >>= \case
Left _ -> return ()
Right (_, _, a :: a) -> do
(b :: b) <- liftIO $ reply a
let !msg = encode b
send' rep [] msg
-- | Request a reply.
--
-- See 'RequestReply' for an example.
--
-- Throws an error when the response cannot be decoded.
request :: forall a b.
RequestReply a b
=> String -- ^ Address of the REP socket
-> a -- ^ The request
-> IO b -- ^ The reply
request addr x = runZMQ $ do
req <- socket Req
connect req addr
let !msg = encode x
send' req [] msg
bs <- receive req
return $! decode (BL.fromStrict bs)