下面是使用反應式香蕉庫的Haskell FRP程序示例。我只是剛剛開始對Haskell感覺如何,特別是沒有把我的頭腦放在FRP的含義上。我會很感激下面我是否在使用反應式香蕉?
{-# 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
代碼的一些批評有一個要點這裏:https://gist.github.com/1099712。
我特別歡迎任何關於這是否是「好」使用accumE的評論,(我不清楚這個函數每次都會遍歷整個事件流,儘管我猜測不到)。
另外我想知道如何從多個套接字中獲取消息 - 目前我有一個永久的事件循環。作爲一個具體的例子,我將如何添加第二個套接字(用zeromq說法的REQ/REP對)來查詢計數器內的IdMap的當前狀態?
感謝海因裏希,原因是在FRP看起來很合適,如果你有很多zeromq套接字,他們可以非常容易地開始像一個GUI的事件驅動輸入...所以我儘管我會踢一些新的想法輪胎:-) 你認爲用State monad和haskell線程做這件事更有意義嗎? –
@Ben Ford:我在答案中加了一個小小的評論。我不知道你想用套接字做什麼,所以我不能告訴你FRP是否爲你的目的矯枉過正。基本上,如果你的事件網絡不會比單獨的'accumE'和一些'filterE'大得多,那麼在沒有FRP的情況下它會更加優雅。 –