Aquí hay un ejemplo del programa FRP de Haskell que usa la biblioteca de plátano reactivo. Solo estoy empezando a sentirme a gusto con Haskell, y especialmente no me he dado cuenta de lo que significa FRP. Me encantaría recibir alguna crítica del código de abajo¿Estoy usando plátano reactivo, verdad?
{-# 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
Hay una esencia aquí: https://gist.github.com/1099712.
Me encantaría especialmente cualquier comentario sobre si este es un "buen" uso de accumE, (no estoy seguro de que esta función recorra todo el flujo de eventos cada vez, aunque supongo que no).
También me gustaría saber cómo se podría ir sobre tirando en mensajes de tomas múltiples - en el momento tengo un bucle de eventos dentro de un siempre. Como un ejemplo concreto de esto, ¿cómo agregaría el segundo socket (un par REQ/REP en el lenguaje zeromq) para consultar el estado actual del contador interno IdMap?
Gracias Heinrich, la razón th En FRP parecía un buen ajuste aquí es que si tiene muchos zócalos zeromq, podrían fácilmente comenzar a parecerse a la entrada guiada por eventos desde una GUI ... Así que pensé en patear los neumáticos de algunas ideas nuevas :-) ¿Creerías que tiene más sentido hacer esto con una mónada estatal y los hilos haskell? –
@ Ben Ford: He agregado un pequeño comentario a la respuesta. No sé qué quiere hacer con los enchufes, por lo que no puedo decirle si FRP es excesivo para su propósito. Básicamente, si su red de eventos no crecerá mucho más grande que esta con un solo 'accumE' y algunos 'filtersE', entonces se realizará de forma más elegante sin FRP. –