men*_*ics 15 haskell reactive-programming reactive-banana
我们有这样的代码:
guiState :: Discrete GuiState
guiState = stepperD (GuiState []) $
union (mkGuiState <$> changes model) evtAutoLayout
evtAutoLayout :: Event GuiState
evtAutoLayout = fmap fromJust . filterE isJust . fmap autoLayout $ changes guiState
Run Code Online (Sandbox Code Playgroud)
你可以看到evtAutoLayout提供给guiState,它被输入到evtAutoLayout中 - 所以那里有一个循环.这是故意的.自动布局调整gui状态直到达到平衡,然后它返回Nothing,因此它应该停止循环.当然,新的模型改变可以再次启动它.
但是,当我们将它们组合在一起时,我们会在编译函数调用中遇到无限循环.即使autoLayout = Nothing,它仍会在编译期间导致堆栈溢出.
如果我删除了guiState中的union调用并删除了图片中的evtAutoLayout ...
guiState :: Discrete GuiState
guiState = stepperD (GuiState []) $ mkGuiState <$> changes model
Run Code Online (Sandbox Code Playgroud)
它工作正常.
有什么建议?
Hei*_*mus 15
这个问题
reactive-banana库是否支持递归定义的事件?
不仅有一个,而且有三个答案.简短的回答是:1.一般不,2.有时是,3.有解决方法是.
这里的答案很长.
反应性香蕉的语义不支持Event
直接用自身定义.
这是Conal Elliott在他最初的FRP语义中做出的决定,我决定坚持下去.它的主要好处是语义仍然非常简单,你可以随时思考
type Behavior a = Time -> a
type Event a = [(Time,a)]
Run Code Online (Sandbox Code Playgroud)
我提供了一个模块Reactive.Banana.Model,它几乎精确地实现了这个模型,你可以查询它的源代码,以获得有关reactive-banana语义的任何问题.特别是,您可以使用它来推断您的示例:使用笔和纸计算或在GHCi中尝试(使用一些模拟数据)将告诉您该值evtAutoLayout
等于_|_
,即未定义.
后者可能会令人惊讶,但正如您所写的那样,该示例确实未定义:GUI状态仅在evtAutoLayout
事件发生时才会发生变化,但只有在您知道GUI状态是否发生变化时才会发生变化,反过来,等等.需要通过插入一个小延迟来打破勒死反馈循环.不幸的是,反应性香蕉目前没有提供插入小延迟的方法,主要是因为我不知道如何以[(Time,a)]
允许递归的方式描述模型方面的小延迟.(但见答案3.)
可以并且鼓励Event
根据Behavior
再次引用事件的方式来定义.换句话说,只要您通过行为,就允许递归.
一个简单的例子就是
import Reactive.Banana.Model
filterRising :: (FRP f, Ord a) => Event f a -> Event f a
filterRising eInput = eOutput
where
eOutput = filterApply (greater <$> behavior) eInput
behavior = stepper Nothing (Just <$> eOutput)
greater Nothing _ = True
greater (Just x) y = x < y
example :: [(Time,Int)]
example = interpretTime filterRising $ zip [1..] [2,1,5,4,8,9,7]
-- example = [(1.0, 2),(3.0, 5),(5.0, 8),(6.0, 9)]
Run Code Online (Sandbox Code Playgroud)
给定事件流,该函数filterRising
仅返回大于先前返回的事件.这在stepper
函数的文档中有所暗示.
但是,这可能不是您想要的那种递归.
仍然可以在反应香蕉中插入小延迟,它不是核心库的一部分,因此没有任何保证的语义.此外,您需要从事件循环中获得一些支持才能执行此操作.
例如,您可以使用wxTimer在处理完当前事件后立即安排事件.所述Wave.hs示例演示递归使用具有反应性香蕉一个wxTimer的.我不太清楚当你将定时器间隔设置0
为时会发生什么,但是它可能执行得太早.您可能需要进行一些实验才能找到一个好的解决方案.
希望有所帮助; 随时要求澄清,例子等
披露:我是反应香蕉库的作者.