Учитывая ваш пример использования, я думаю, у вас все будет хорошо, если вы будете держаться подальше от fromPoll
. Чтобы объяснить почему, необходимо сделать несколько пояснений. (Примечание: далее «поток» относится к Event t a
, а «появление» — к одному из составляющих их срабатываний.)
Однако опрос поведения с событиями (через <@
и т. д.) дает мне значение поведения из предыдущего события, а не текущее значение.
Я полагаю, вы намекаете на подобные объяснения из документации для stepper
:
Обратите внимание, что знак «меньше» в сравнении timex < time
означает, что значение поведения изменяется «слегка после» возникновения события. Это позволяет использовать рекурсивные определения.
Однако эта задержка касается только потока, используемого для определения поведения (то есть того, который вы передаете stepper
/accumB
) и любых потоков, синхронизированных с ним. Например, предположим, что у вас есть два независимых потока, eTick
и eTock
, и следующий сетевой фрагмент:
eIncrement = (+1) <$ eTick
bCount = accumB 0 eIncrement
eCountTick = bCount <@ eTick
eCountTock = bCount <@ eTock
eIncrement
и eCountTick
синхронизированы с eTick
, поэтому значение, наблюдаемое через eCountTick
, является "старым" значением; то есть значение до синхронизированного обновления. Однако с точки зрения eCountTock
все это не имеет значения. Наблюдатель, использующий eCountTock
, не может говорить о задержке, и значение всегда является текущим.
Поведение, наблюдаемое с fromPoll
, не может зависеть от себя, поэтому нельзя ввести циклы, наблюдая за поведением непосредственно перед срабатыванием этого события, а не сразу после срабатывания предыдущего события.
Нас интересуют только потоки, синхронизированные с тем, который обновляет поведение. Таким образом, поскольку наблюдаемые значения «непосредственно перед следующим событием» и «сразу после предыдущего события» сводятся к одному и тому же. fromPoll
, однако, немного запутывает ситуацию. Он создает поведение, которое обновляется всякий раз, когда в сети событий происходит любое событие; а так обновления синхронизируются с объединением всех потоков. Не существует такой вещи, как поток, независимый от события fromPoll
, и поэтому наблюдаемое значение будет зависеть от задержки, какой бы мы ее ни наблюдали. Таким образом, fromPoll
не будет работать для часов, управляемых приложением, которые требуют отслеживания непрерывных изменений с некоторой точностью.
Во всем вышесказанном подразумевается, что reactive-banana не имеет встроенного понятия времени. В каждом потоке есть только «логические» линии времени, которые могут быть переплетены путем слияния потоков. Поэтому, если нам нужно текущее поведение во времени, лучше всего построить его из независимого потока. Вот демонстрация этого подхода, который будет давать свежие и своевременные результаты, насколько позволяет точность threadDelay
:
{-# LANGUAGE RankNTypes #-}
module Main where
import Control.Concurrent
import Control.Monad
import Data.Time
import Reactive.Banana
import Reactive.Banana.Frameworks
main = do
let netDesc :: forall t. Frameworks t => Moment t ()
netDesc = do
(eTime, fireTime) <- newEvent
liftIO . forkIO . forever $
threadDelay (50 * 1000) >> getCurrentTime >>= fireTime
bTime <- flip stepper eTime <$> liftIO getCurrentTime
(eTick, fireTick) <- newEvent
liftIO . forkIO . forever $
threadDelay (5000 * 1000) >> fireTick ()
reactimate $ print <$> bTime <@ eTick
network <- compile netDesc
actuate network >> threadDelay (52000 * 1000) >> pause network
bTime
обновляется через eTime
каждые 0,05 с; это наблюдается через eTick
, поток, независимый от eTime
, с появлением каждые 5 секунд. Затем вы можете использовать eTick
и полученные от него потоки для наблюдения и обновления ваших сущностей. В качестве альтернативы вы можете комбинировать bTime
и поведение объекта в аппликативном стиле, чтобы получить, например. поведения для последних пингов, которые следует наблюдать с помощью eTick
.
В вашем случае каноническое поведение времени выглядит разумным; он концептуально ясен и легко обобщается для нескольких тиков. В любом случае, другие подходы, с которыми вы можете поиграть, включают избавление от bTime
и использование eTick
в качестве потока текущего времени с низким разрешением (хотя это, кажется, ускоряет накопление threadDelay
ошибок) и избавление от eTick
с помощью changes
на получить поток свежеобновленных значений из поведения (поскольку у него есть свои причуды и неприятности, как намекает документация).
person
duplode
schedule
12.06.2014