我们为什么要在FRP中使用行为 [英] Why should we use Behavior in FRP

查看:235
本文介绍了我们为什么要在FRP中使用行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习反应香蕉。为了理解库,我决定实现一个虚拟应用程序,只要有人按下按钮就会增加计数器。



我使用的UI库是Gtk,但是与解释无关。



以下是我提出的非常简单的实现:

  import Graphics.UI.Gtk 
import Reactive.Banana
import Reactive.Banana.Frameworks

makeNetworkDescription addEvent = do
eClick < - fromAddHandler addEvent
reactimate $(putStrLn。show)< $> (accumE 0((+1)<$ eClick))

main :: IO()
main = do
(addHandler,fireEvent)< - newAddHandler
initGUI
网络< - 编译$ makeNetworkDescription addHandler
启动网络
窗口< - windowNew
按钮< - buttonNew
设置窗口[containerBorderWidth:= 10 ,containerChild:= button]
set button [buttonLabel:=Add One]
onClicked按钮$ fireEvent()
onDestroy窗口mainQuit
widgetShowAll窗口
mainGUI

这只是将结果转储到shell中。我想出了这个解决方案,阅读Heinrich Apfelmus的文章。请注意,在我的示例中,我没有使用过一个行为



在文章中有一个示例网络:

  makeNetworkDescription addKeyEvent = do 
eKey< - fromAddHandler addKeyEvent
let
eOctaveChange = filterMapJust getOc​​taveChange eKey
bOctave = accumB 3(changeOctave< $> eOctaveChange)
ePitch = filterMapJust(`lookup` charPitches)eKey
bPitch =步进PC ePitch
b注=注意< $> bOctave< *> bPitch
eNoteChanged< - changes bNote
reactimate'$ fmap(\\\
- > putStrLn(Now playing++ show n))
< $> eNoteChanged

这个例子展示了一个 stepper 一个事件转换为行为并使用事件 更改。在上面的例子中,我们可以只使用 Event ,我想这不会有什么区别(除非我不理解)。

那么有人可以阐明何时使用 Behavior 以及为什么?我们应该尽快转换所有 Event s吗?



在我的小实验中,我没有看到行为可以使用。

谢谢

解决方案

任何时候FRP网络都会在反应香蕉中做些什么,这是因为它对一些输入事件有反应。在系统外部进行任何可观察的事情的唯一方式是通过连接外部系统来响应它产生的事件(使用 reactimate )。



因此,如果您所做的只是通过生成输出事件对输入事件做出反应,那么不会,您会发现使用 Behavior
$ p code>行为
对于生成取决于多个事件流的程序行为非常有用,记住事件发生在不同时间



一个事件具体价值的具体时刻。一个行为在所有时间点都有一个值,没有特殊的时间点(除了变化,这是很方便的,但破坏了一些模型)。

一个很简单的例子,很多GUI都很熟悉,如果我想对鼠标点击作出反应,并按shift-click做某事与不保持换档键时的点击不同。用一个行为保存一个值,指示是否按住shift键,这很简单。如果我只有 Event s用于shift键的按下/释放以及鼠标点击,那就更难了。越来越难,它的水平更低。为什么我必须做复杂的摆弄才能实现一个简单的概念,如shift-click? 行为事件之间的选择是一个有用的抽象概念,用于实现程序的概念它更贴近你在编程世界之外对它们的看法。

这里的一个例子是游戏世界中的可移动对象。我可以 有一个 Event Position 来代表它移动的所有时间。或者我可以有一个行为定位来代表它始终在哪里。通常我会把对象看作在任何时候都拥有的位置,所以 Behavior 是一个更好的概念契合。



另一个地方 Behavior s对于表示程序可以进行的外部观察非常有用,只能检查当前值因为外部系统不会在发生变化时通知您)。



例如,假设您的程序必须密切注意温度传感器并避免开始工作当温度过高时。使用事件温度,我将决定多久查询温度传感器(或回应什么)。然后在我的其他例子中遇到与手动做某些事情相同的问题,以便使事件中最后的温度读数可用,从而决定是否开始工作。或者我可以使用 fromPoll 来制作一个行为温度。现在我已经得到了一个代表温度随时间变化的值,并且我完全从投票传感器中抽象出来;反应型香蕉本身就可以根据需要频繁地轮询传感器,而无需为此需要任何逻辑!


I am learning reactive-banana. In order to understand the library I have decide to implement a dummy application that would increase a counter whenever someone pushes a button.

The UI library I am using is Gtk but that is not relevant for the explanation.

Here is the very simple implementation that I have come up with:

import Graphics.UI.Gtk
import Reactive.Banana
import Reactive.Banana.Frameworks

makeNetworkDescription addEvent = do
    eClick <- fromAddHandler addEvent
    reactimate $ (putStrLn . show) <$> (accumE 0 ((+1) <$ eClick))

main :: IO ()
main = do
    (addHandler, fireEvent) <- newAddHandler
    initGUI
    network <- compile $ makeNetworkDescription addHandler
    actuate network
    window <- windowNew
    button <- buttonNew
    set window [ containerBorderWidth := 10, containerChild := button ]
    set button [ buttonLabel := "Add One" ]
    onClicked button $ fireEvent ()
    onDestroy window mainQuit
    widgetShowAll window
    mainGUI

This just dumps the result in the shell. I came up to this solution reading the article by Heinrich Apfelmus. Notice that in my example I have not used a single Behavior.

In the article there is an example of a network:

makeNetworkDescription addKeyEvent = do
    eKey <- fromAddHandler addKeyEvent
    let
        eOctaveChange = filterMapJust getOctaveChange eKey
        bOctave = accumB 3 (changeOctave <$> eOctaveChange)
        ePitch = filterMapJust (`lookup` charPitches) eKey
        bPitch = stepper PC ePitch
        bNote = Note <$> bOctave <*> bPitch
    eNoteChanged <- changes bNote
    reactimate' $ fmap (\n -> putStrLn ("Now playing " ++ show n))
               <$> eNoteChanged

The example show a stepper that transforms an Event into a Behavior and brings back an Event using changes. In the above example we could have used only Event and I guess that it would have made no difference (unless I am not understanding something).

So could someone can shed some light on when to use Behavior and why? Should we convert all Events as soon as possible?

In my little experiment I don't see where Behavior can be used.

Thanks

解决方案

Anytime the FRP network "does something" in Reactive Banana it's because it's reacting to some input event. And the only way it does anything observable outside the system is by wiring up an external system to react to events it generates (using reactimate).

So if all you're doing is immediately reacting to an input event by producing an output event, then no, you won't find much reason to use Behaviour.

Behaviour is very useful for producing program behaviour that depends on multiple event streams, where you have to remember that events happen at different times.

An Event has occurrences; specific instants of time where it has a value. A Behaviour has a value at all points in time, with no instants of time that are special (except with changes, which is convenient but kind of model-breaking).

A simple example familiar from many GUIs would be if I want to react to mouse clicks and have shift-click do something different from a click when the shift key is not held. With a Behaviour holding a value indicating whether the shift key is held down, this is trivial. If I just had Events for shift key press/release and for mouse clicks it's much harder.

In addition to being harder, it's much more low level. Why should I have to do complicated fiddling just to implement a simple concept like shift-click? The choice between Behaviour and Event is a helpful abstraction for implementing your program's concepts in terms that map more closely to the way you think about them outside the programming world.

An example here would be a movable object in a game world. I could have an Event Position representing all the times it moves. Or I could just have a Behaviour Position representing where it is at all times. Usually I'll be thinking of the object as having a position at all times, so Behaviour is a better conceptual fit.

Another place Behaviours are useful is for representing external observations your program can make, where you can only check the "current" value (because the external system won't notify you when changes occur).

For an example, let's say your program has to keep tabs on a temperature sensor and avoid starting a job when the temperature is too high. With an Event Temperature I'll have decide up front how often to poll the temperature sensor (or in response to what). And then have all the same issues as in my other examples about having to manually do something to make the last temperature reading available to the event that decides whether or not to start a job. Or I could use fromPoll to make a Behaviour Temperature. Now I've got a value that represents the time-varying value of the temperature, and I've completely abstracted away from polling the sensor; Reactive Banana itself takes care of polling the sensor as often as it might be needed without me needing to impending any logic for that at all!

这篇关于我们为什么要在FRP中使用行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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