haskell zeromq reactive-programming frp reactive-banana

haskell - ¿Estoy usando reactivo-banana verdad?



zeromq reactive-programming (1)

Aquí hay un ejemplo del programa Haskell FRP utilizando la biblioteca reactive-banana. Simplemente estoy empezando a sentirme a mi manera con Haskell, y especialmente no me he dado cuenta de lo que significa FRP. Realmente apreciaría alguna crítica del código de abajo

{-# LANGUAGE DeriveDataTypeable #-} module Main where {- Example FRP/zeromq app. The idea is that messages come into a zeromq socket in the form "id state". The state is of each id is tracked until it''s complete. -} import Control.Monad import Data.ByteString.Char8 as C (unpack) import Data.Map as M import Data.Maybe import Reactive.Banana import System.Environment (getArgs) import System.ZMQ data Msg = Msg {mid :: String, state :: String} deriving (Show, Typeable) type IdMap = Map String String -- | Deserialize a string to a Maybe Msg fromString :: String -> Maybe Msg fromString s = case words s of (x:y:[]) -> Just $ Msg x y _ -> Nothing -- | Map a message to a partial operation on a map -- If the ''state'' of the message is "complete" the operation is a delete -- otherwise it''s an insert toMap :: Msg -> IdMap -> IdMap toMap msg = case msg of Msg id_ "complete" -> delete id_ _ -> insert (mid msg) (state msg) main :: IO () main = do (socketHandle,runSocket) <- newAddHandler args <- getArgs let sockAddr = case args of [s] -> s _ -> "tcp://127.0.0.1:9999" putStrLn ("Socket: " ++ sockAddr) network <- compile $ do recvd <- fromAddHandler socketHandle let -- Filter out the Nothings justs = filterE isJust recvd -- Accumulate the partially applied toMap operations counter = accumE M.empty $ (toMap . fromJust <$> justs) -- Print the contents reactimate $ fmap print counter actuate network -- Get a socket and kick off the eventloop withContext 1 $ /ctx -> withSocket ctx Sub $ /sub -> do connect sub sockAddr subscribe sub "" linkSocketHandler sub runSocket -- | Recieve a message, deserialize it to a ''Msg'' and call the action with the message linkSocketHandler :: Socket a -> (Maybe Msg -> IO ()) -> IO () linkSocketHandler s runner = forever $ do receive s [] >>= runner . fromString . C.unpack

Aquí hay una idea: https://gist.github.com/1099712 .

En particular, agradecería cualquier comentario sobre si este es un uso "bueno" de la acumulación, (no estoy seguro de que esta función atravesará todo el flujo de eventos cada vez, aunque supongo que no).

También me gustaría saber cómo se manejaría uno al tirar de los mensajes desde múltiples sockets, en este momento tengo un bucle de eventos dentro de una eternidad. Como ejemplo concreto de esto, ¿cómo agregaría un segundo socket (un par REQ / REP en el lenguaje zeromq) para consultar el estado actual del contador interno de IdMap?


(Autor de habla reactive-banana .)

En general, tu código me parece bien. En realidad, no entiendo por qué estás usando Banana reactiva en primer lugar, pero tendrás tus razones. Dicho esto, si está buscando algo como Node.js, recuerde que los hilos ligeros de Haskell hacen innecesario el uso de una arquitectura basada en eventos.

Anexo: Básicamente, la programación reactiva funcional es útil cuando tiene una variedad de diferentes entradas, estados y salidas que deben funcionar junto con la sincronización correcta (piense en las GUI, animaciones, audio). En contraste, es una exageración cuando se trata de muchos eventos esencialmente independientes; estos se manejan mejor con funciones ordinarias y el estado ocasional.

Con respecto a las preguntas individuales:

"Me encantaría cualquier comentario sobre si este es un uso" bueno "de la acumulación, (no estoy seguro de que esta función atravesará todo el flujo de eventos cada vez, aunque supongo que no)".

Luce bien para mi. Como has adivinado, la función de accumE es de hecho en tiempo real; Sólo almacenará el valor acumulado actual.

A juzgar por lo que crees, parece que piensas que cada vez que entra un nuevo evento, viajará a través de la red como una luciérnaga. Si bien esto sucede internamente, no es así como debe pensar acerca de la programación reactiva funcional. Más bien, la imagen correcta es la siguiente: el resultado de fromAddHandler es la lista completa de eventos de entrada, tal como ocurrirán . En otras palabras, debe pensar que recvd contiene la lista ordenada de todos y cada uno de los eventos del futuro. (Por supuesto, en aras de su propia cordura, no debe intentar mirarlos antes de que haya llegado su hora ;-)) La función de accumE simplemente transforma una lista en otra al atravesarla una vez.

Tendré que aclarar más esta forma de pensar en la documentación.

"También me gustaría saber cómo se haría para obtener mensajes desde múltiples sockets, en este momento tengo un bucle de eventos dentro de una eternidad. Como ejemplo concreto de esto, ¿cómo agregaría un segundo socket (un par REQ / REP? en zeromq parlance) para consultar el estado actual del IdMap dentro del contador? "

Si la función de receive no se bloquea, simplemente puede llamarlo dos veces en diferentes sockets

linkSocketHandler s1 s2 runner1 runner2 = forever $ do receive s1 [] >>= runner1 . fromString . C.unpack receive s2 [] >>= runner2 . fromString . C.unpack

Si se bloquea, deberá usar subprocesos, consulte también la sección Manejo de múltiples transmisiones TCP en el libro Real World Haskell. (No dude en hacer una nueva pregunta sobre esto, ya que está fuera del alcance de este).