我使用反应香蕉吗? [英] Am I using reactive-banana right?

查看:153
本文介绍了我使用反应香蕉吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个使用反应式香蕉库的Haskell FRP程序示例。我只是刚刚开始对Haskell感觉如何,特别是没有把我的头脑放在FRP的含义上。我真的很感谢下面的代码的一些批评

  { - #LANGUAGE DeriveDataTypeable# - } 
module Main

{ -
示例FRP / zeromq应用程序。

这个想法是消息以id状态的形式进入一个zeromq套接字。每个ID的状态都会被跟踪直至完成。
- }

导入Control.Monad
导入Data.ByteString.Char8作为C(解包)
导入Data.Map作为M
导入数据。也许
导入Reactive.Banana
导入System.Environment(getArgs)
导入System.ZMQ

data Msg = Msg {mid :: String,state :: String }
deriving(Show,Typeable)

类型IdMap =地图字符串字符串

- |将字符串反序列化为Maybe Msg
fromString :: String - >也许Msg
fromString s =

(x:y:[]) - >只需$ Msg x y
_ - >没有

- |将消息映射到地图上的部分操作
- 如果消息的状态为完成,则操作为删除
- 否则为插入
toMap ::消息 - > IdMap - > IdMap
toMap msg = case bg
Msg id_complete - >删除id_
_ - > insert(mid msg)(state msg)
$ b $ main :: IO()
main = do
(socketHandle,runSocket)< - newAddHandler

args< - getArgs
let sockAddr =
[s] - >> s
_ - > tcp://127.0.0.1:9999
putStrLn(Socket:++ sockAddr)


网络< - 编译$ do
recvd< ; - fromAddHandler socketHandle

let
- 过滤掉Nothings
justs = filterE isJust recvd
- 累计部分应用的toMap操作
counter = accumE M.empty $(toMap。fromJust< $> justs)


- 打印内容
reactimate $ fmap打印计数器

激活网络

- 获取套接字并启动事件回发
withContext 1 $ \ctct - >
withSocket ctx Sub $ \sub - > do
connect sub sockAddr
subscribe sub
linkSocketHandler sub runSocket


- |收到一条消息,将其反序列化为一个'Msg',并用消息
linkSocketHandler :: Socket a - > (也许消息 - > IO()) - > IO()
linkSocketHandler s runner = forever $ do
receive s []>>> = runner。 fromString。 C.unpack

这里有一个要点: https://gist.github.com/1099712



我特别欢迎任何意见这是不是一个好的使用accumE,(我不清楚这个函数会遍历整个事件流,虽然我猜测不是每次)。

另外我想知道如何从多个套接字中获取消息 - 目前我有一个事件循环永远在里面。作为一个具体的例子,我将如何添加第二个套接字(在zeromq说法中是一个REQ / REP对)来查询计数器内的IdMap的当前状态?

解决方案

反应性香蕉的发言) p>

总的来说,你的代码对我来说看起来很好。我真的不明白你为什么首先使用反应性香蕉,但你会有你的理由。也就是说,如果你正在寻找类似Node.js的东西,记住Haskell的leightweight线程

附录:基本上,当您有各种不同的输入,状态和输出必须在恰当的时机一起工作(想象GUI,动画,音频)。相比之下,当你处理许多基本上独立的事件时,它是过度的;这些最好用普通函数和偶尔的状态来处理。




关于个别问题:


我特别欢迎任何关于这是否是好使用accumE的评论,(我不清楚这个函数是否会遍历整个事件流虽然我猜不是)。


看起来对我来说很好。正如你猜测的那样, accumE 函数确实是实时的;它只会存储当前的累计值。根据您的猜测,您似乎认为每当有新事件发生时,它就会像网络一样通过网络传播萤火虫。虽然这确实发生在内部,但是关于功能性响应式编程,您应该如何思考 。相反,正确的图像是这样的: fromAddHandler 的结果是输入事件发生的完整列表 。换句话说,您应该认为 recvd 包含未来每个事件的有序列表。 (当然,为了你自己的理智,你不应该在他们的时间到来之前看看它们;;)) accumE 函数简单地将一个通过遍历一次就可以列入另一个列表。



我需要在文档中更清楚地考虑这种思考方式。


另外,我想知道如何从多个套接字中提取消息 - 目前我已经在事件循环内部永远存在了。作为一个具体的例子,我添加第二个套接字(在zeromq说法中是一个REQ / REP对)来查询计数器内的IdMap的当前状态?


如果收到函数没有被阻塞,你可以简单地在不同的套接字上调用它两次

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

如果确实阻塞了,您将需要使用线程,另请参阅在Real World Haskell一书中处理多个TCP流 。 (随意就此提出一个新问题,因为它超出了这个范围。)


Here's an example Haskell FRP program using the reactive-banana library. I'm only just starting to feel my way with Haskell, and especially haven't quite got my head around what FRP means. I'd really appreciate some critique of the code below

{-# 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

There's a gist here: https://gist.github.com/1099712.

I'd particularly welcome any comments around whether this is a "good" use of accumE, (I'm unclear of this function will traverse the whole event stream each time although I'm guessing not).

Also I'd like to know how one would go about pulling in messages from multiple sockets - at the moment I have one event loop inside a forever. As a concrete example of this how would I add second socket (a REQ/REP pair in zeromq parlance) to query to the current state of the IdMap inside counter?

解决方案

(Author of reactive-banana speaking.)

Overall, your code looks fine to me. I don't actually understand why you are using reactive-banana in the first place, but you'll have your reasons. That said, if you are looking for something like Node.js, remember that Haskell's leightweight threads make it unnecessary to use an event-based architecture.

Addendum: Basically, functional reactive programming is useful when you have a variety of different inputs, states and output that must work together with just the right timing (think GUIs, animations, audio). In contrast, it's overkill when you are dealing with many essentially independent events; these are best handled with ordinary functions and the occasional state.


Concerning the individual questions:

"I'd particularly welcome any comments around whether this is a "good" use of accumE, (I'm unclear of this function will traverse the whole event stream each time although I'm guessing not)."

Looks fine to me. As you guessed, the accumE function is indeed real-time; it will only store the current accumulated value.

Judging from your guess, you seem to be thinking that whenever a new event comes in, it will travel through the network like a firefly. While this does happen internally, it is not how you should think about functional reactive programming. Rather, the right picture is this: the result of fromAddHandler is the complete list of input events as they will happen. In other words, you should think that recvd contains the ordered list of each and every event from the future. (Of course, in the interest of your own sanity, you shouldn't try to look at them before their time has come. ;-)) The accumE function simply transforms one list into another by traversing it once.

I will need to make this way of thinking more clear in the documentation.

"Also I'd like to know how one would go about pulling in messages from multiple sockets - at the moment I have on event loop inside a forever. As a concrete example of this how would I add second socket (a REQ/REP pair in zeromq parlance) to query to the current state of the IdMap inside counter?"

If the receive function does not block, you can simply call it twice on different sockets

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

If it does block, you will need to use threads, see also the section Handling Multiple TCP Streams in the book Real World Haskell. (Feel free to ask a new question on this, as it is outside the scope of this one.)

这篇关于我使用反应香蕉吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆