简体   繁体   中英

How to use a ZMQ subscriber in Haskell with pipes

I can get a ZMQ subscriber to work in Haskell, but would appreciate guidance on how to use that data with Pipes. My attempt at writing a Producer fails at 'stack build' with the following error:

Couldn't match type 'Proxy X () c'0 c0 (ZMQ z)' with 'ZMQ z'

Expected type: ZMQ z ()

Actual type: Proxy X () c'0 c0 (ZMQ z) ()

{-# LANGUAGE OverloadedStrings #-}
module Main where
import Control.Monad
import Pipes
import qualified Pipes.Prelude as P
import System.ZMQ4.Monadic
import qualified Data.ByteString.Char8 as CS

fromZMQ :: (Receiver r) => Socket z r -> Producer String (ZMQ z) ()
fromZMQ sock = do
    msg <- lift $ receive sock
    yield (CS.unpack msg)
    fromZMQ sock

main :: IO ()
main = --do
  runZMQ $ do
    subSock <- socket Sub  ---subscriptionSocket
    subscribe subSock ""
    connect subSock "tcp://127.0.0.1:4998" 
    forever $ fromZMQ subSock >-> P.take 3 >-> P.print

Note, I want to consume data being published on ZMQ by a python script.

Working off Thomas' suggestion of using Chan, I adapted the MVar example (link below) to accumulate the strings received and a count of them to demonstrate updating and reading state

https://www.oreilly.com/library/view/parallel-and-concurrent/9781449335939/ch07.html

{-# LANGUAGE OverloadedStrings #-}

module Main where

import Control.Concurrent  
import Control.Monad
import System.ZMQ4.Monadic
import qualified Data.ByteString.Char8 as CS

newtype State = State (MVar (Int, [CS.ByteString]) ) --(count, list of strings received over zmq)

newState :: IO State
newState = do
  m <- newMVar (0, [])
  return (State m)

updateState :: State -> CS.ByteString -> IO ()
updateState (State m) newString = do
  (count,strList) <- takeMVar m
  putMVar m ( count + 1 , strList ++ [newString] )

showState :: State -> IO String
showState (State m) = do
  count <- takeMVar m
  putMVar m count  --return the lock; no changes
  return (show count)

main = runZMQ $ do
    sub <- socket Sub
    subscribe sub ""
    connect sub "tcp://127.0.0.1:4998"
    s <- liftIO newState 
    forever $ do
      receive sub >>= liftIO . updateState s 
      liftIO $ updateState s "hello"  --'manually' add an additional string on each iteration
      op <- liftIO $ showState s
      liftIO $ print op 

The only problem in your code is the last line.

You have a “pipe” there:

fromZMQ subSock >-> P.take 3 >-> P.print :: Effect (ZMQ z) ()

and when you apply forever to it, the type remains the same. But what you need there is a simple ZMQ z , in other words, you need to actually perform the pipes computation and you use the runEffect function for this. As a side note, you don't really need the forever as the stream will never end anyway.

So, all you have to do, really, is replace forever with runEffect in the last line.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM