module FRP.Sodium.Plain where
import qualified FRP.Sodium.Context as R
import Control.Applicative
import Control.Concurrent.Chan
import Control.Concurrent.MVar (MVar, newEmptyMVar, takeMVar, newMVar, readMVar)
import qualified Control.Concurrent.MVar as MV
import Control.Exception (evaluate)
import Control.Monad
import Control.Monad.State.Strict
import Control.Monad.Trans
import Data.Int
import Data.IORef
import Data.Map (Map)
import qualified Data.Map as M
import Data.Maybe
import Data.Set (Set)
import qualified Data.Set as S
import Data.Sequence (Seq, (|>), (><))
import qualified Data.Sequence as Seq
import GHC.Exts
import System.Mem.Weak
import System.IO.Unsafe
import Unsafe.Coerce
modifyMVar :: MVar a -> (a -> IO (a, b)) -> IO b
modifyMVar mv f = MV.modifyMVar mv $ \a -> do
(a', b') <- f a
evaluate a'
return (a', b')
modifyMVar_ :: MVar a -> (a -> IO a) -> IO ()
modifyMVar_ mv f = MV.modifyMVar_ mv $ \a -> do
a' <- f a
evaluate a'
return a'
putMVar :: MVar a -> a -> IO ()
putMVar mv a = do
evaluate a
MV.putMVar mv a
unsafeNewIORef :: a -> b -> IORef a
unsafeNewIORef v dummy = unsafePerformIO (newIORef v)
data Plain
partition :: Partition
partition = unsafePerformIO createPartition
where
createPartition :: IO Partition
createPartition = do
lock <- newEmptyMVar
nextNodeIDRef <- newIORef (NodeID 0)
return $ Partition {
paLock = lock,
paNextNodeID = nextNodeIDRef
}
type Reactive = R.Reactive Plain
type Event = R.Event Plain
type Behavior = R.Behavior Plain
type Behaviour = R.Behavior Plain
data Sample a = Sample {
unSample :: IO a,
sDep :: Dep,
sampleKeepAlive :: Maybe (IORef ())
}
instance R.Context Plain where
newtype Reactive Plain a = Reactive (StateT ReactiveState IO a)
data Event Plain a = Event {
getListenRaw :: Reactive (Listen a),
evCacheRef :: IORef (Maybe (Listen a)),
eDep :: Dep
}
data Behavior Plain a = Behavior {
updates_ :: Event a,
sampleImpl :: Sample a
}
sync = sync
newEvent = newEvent
listen = listen
never = never
merge = merge
filterJust = filterJust
hold = hold
updates = updates
value = value
snapshot = snapshot
switchE = switchE
switch = switch
execute = execute
sample = sample
coalesce = coalesce
once = once
split = split
updates :: Behavior a -> Event a
updates = coalesce (flip const) . updates_
sync :: Reactive a -> IO a
sync task = do
let loop :: StateT ReactiveState IO () = do
queue1 <- gets asQueue1
if not $ Seq.null queue1 then do
let Reactive task = Seq.index queue1 0
modify $ \as -> as { asQueue1 = Seq.drop 1 queue1 }
task
loop
else do
queue2 <- gets asQueue2
mTask <- lift $ popPriorityQueue queue2
case mTask of
Just (Reactive task) -> do
task
loop
Nothing -> do
final <- gets asFinal
if not $ Seq.null final then do
let Reactive task = Seq.index final 0
modify $ \as -> as { asFinal = Seq.drop 1 final }
task
loop
else
return ()
post <- gets asPost
unless (Seq.null post) $ do
let Reactive task = post `Seq.index` 0
modify $ \as -> as { asPost = Seq.drop 1 post }
task
loop
outVar <- newIORef undefined
let lock = paLock partition
putMVar lock ()
q <- newPriorityQueue
evalStateT loop $ ReactiveState {
asQueue1 = Seq.singleton (task >>= ioReactive . writeIORef outVar),
asQueue2 = q,
asFinal = Seq.empty,
asPost = Seq.empty
}
takeMVar lock
readIORef outVar
newEvent :: Reactive (Event a, a -> Reactive ())
newEvent = do
(ev, push, _) <- ioReactive $ newEventLinked undefined
return (ev, push)
listen :: Event a -> (a -> IO ()) -> Reactive (IO ())
listen ev handle = listenTrans ev $ \a -> ioReactive (handle a >> touch ev)
never :: Event a
never = Event {
getListenRaw = return $ Listen (\_ _ _ -> return (return ())) undefined,
evCacheRef = unsafeNewIORef Nothing undefined,
eDep = undefined
}
merge :: Event a -> Event a -> Event a
merge ea eb = Event gl cacheRef (dep (ea, eb))
where
cacheRef = unsafeNewIORef Nothing eb
gl = do
(l, push, nodeRef) <- ioReactive newEventImpl
unlistener <- later $ do
u1 <- linkedListen ea (Just nodeRef) False push
u2 <- linkedListen eb (Just nodeRef) False $
schedulePrioritized (Just nodeRef) . push
return (u1 >> u2)
addCleanup_Listen unlistener l
filterJust :: Event (Maybe a) -> Event a
filterJust ema = Event gl cacheRef (dep ema)
where
cacheRef = unsafeNewIORef Nothing ema
gl = do
(l, push, nodeRef) <- ioReactive newEventImpl
unlistener <- later $ linkedListen ema (Just nodeRef) False $ \ma -> case ma of
Just a -> push a
Nothing -> return ()
addCleanup_Listen unlistener l
hold :: a -> Event a -> Reactive (Behavior a)
hold initA ea = do
bsRef <- ioReactive $ newIORef $ initA `seq` BehaviorState initA Nothing
unlistener <- later $ linkedListen ea Nothing False $ \a -> do
bs <- ioReactive $ readIORef bsRef
ioReactive $ writeIORef bsRef $ a `seq` bs { bsUpdate = Just a }
when (isNothing (bsUpdate bs)) $ scheduleLast $ ioReactive $ do
bs <- readIORef bsRef
let newCurrent = fromJust (bsUpdate bs)
writeIORef bsRef $ newCurrent `seq` BehaviorState newCurrent Nothing
keepAliveRef <- ioReactive $ newIORef ()
sample <- ioReactive $ addCleanup_Sample unlistener
(Sample (bsCurrent <$> readIORef bsRef) (dep ea) (Just keepAliveRef))
let beh = sample `seq` Behavior {
updates_ = ea,
sampleImpl = sample
}
return beh
value :: Behavior a -> Event a
value ba = sa `seq` ea `seq` eventify (listenValueRaw ba) (dep (sa, ea))
where
sa = sampleImpl ba
ea = updates ba
snapshot :: (a -> b -> c) -> Event a -> Behavior b -> Event c
snapshot f ea bb = sample' `seq` Event gl cacheRef (dep (ea, sample))
where
cacheRef = unsafeNewIORef Nothing bb
sample = sampleImpl bb
sample' = unSample sample
gl = do
(l, push, nodeRef) <- ioReactive newEventImpl
unlistener <- later $ linkedListen ea (Just nodeRef) False $ \a -> do
b <- ioReactive sample'
push (f a b)
addCleanup_Listen unlistener l
switchE :: Behavior (Event a) -> Event a
switchE bea = eea `seq` Event gl cacheRef (dep (eea, depRef))
where
eea = updates bea
cacheRef = unsafeNewIORef Nothing bea
depRef = unsafeNewIORef undefined bea
gl = do
(l, push, nodeRef) <- ioReactive newEventImpl
unlisten2Ref <- ioReactive $ newIORef Nothing
let doUnlisten2 = do
mUnlisten2 <- readIORef unlisten2Ref
fromMaybe (return ()) mUnlisten2
unlistener1 <- later $ do
initEa <- sample bea
(ioReactive . writeIORef unlisten2Ref) =<< (Just <$> linkedListen initEa (Just nodeRef) False push)
unlisten1 <- linkedListen eea (Just nodeRef) False $ \ea -> scheduleLast $ do
ioReactive $ do
doUnlisten2
writeIORef depRef ea
(ioReactive . writeIORef unlisten2Ref) =<< (Just <$> linkedListen ea (Just nodeRef) True push)
return $ unlisten1 >> doUnlisten2
addCleanup_Listen unlistener1 l
switch :: Behavior (Behavior a) -> Reactive (Behavior a)
switch bba = do
ba <- sample bba
depRef <- ioReactive $ newIORef ba
za <- sample ba
let eba = updates bba
ioReactive $ evaluate eba
(ev, push, nodeRef) <- ioReactive $ newEventLinked (dep (bba, depRef))
unlisten2Ref <- ioReactive $ newIORef Nothing
let doUnlisten2 = do
mUnlisten2 <- readIORef unlisten2Ref
fromMaybe (return ()) mUnlisten2
unlisten1 <- listenValueRaw bba (Just nodeRef) False $ \ba -> do
ioReactive $ do
doUnlisten2
writeIORef depRef ba
(ioReactive . writeIORef unlisten2Ref . Just) =<< listenValueRaw ba (Just nodeRef) False push
hold za $ finalizeEvent ev (unlisten1 >> doUnlisten2)
execute :: Event (Reactive a) -> Event a
execute ev = Event gl cacheRef (dep ev)
where
cacheRef = unsafeNewIORef Nothing ev
gl = do
(l, push, nodeRef) <- ioReactive newEventImpl
unlistener <- later $ linkedListen ev (Just nodeRef) False $ \action -> action >>= push
addCleanup_Listen unlistener l
sample :: Behavior a -> Reactive a
sample beh = ioReactive $ do
let sample = sampleImpl beh
maybe (return ())
readIORef
(sampleKeepAlive sample)
unSample sample
coalesce :: (a -> a -> a) -> Event a -> Event a
coalesce combine e = Event gl cacheRef (dep e)
where
cacheRef = unsafeNewIORef Nothing e
gl = do
(l, push, nodeRef) <- ioReactive newEventImpl
outRef <- ioReactive $ newIORef Nothing
unlistener <- later $ linkedListen e (Just nodeRef) False $ \a -> do
first <- isNothing <$> ioReactive (readIORef outRef)
ioReactive $ modifyIORef outRef $ \ma -> Just $ case ma of
Just a0 -> a0 `combine` a
Nothing -> a
when first $ schedulePrioritized (Just nodeRef) $ do
Just out <- ioReactive $ readIORef outRef
ioReactive $ writeIORef outRef Nothing
push out
addCleanup_Listen unlistener l
once :: Event a -> Event a
once e = Event gl cacheRef (dep e)
where
cacheRef = unsafeNewIORef Nothing e
gl = do
(l, push, nodeRef) <- ioReactive newEventImpl
aliveRef <- ioReactive $ newIORef True
unlistener <- later $ do
rec
unlisten <- linkedListen e (Just nodeRef) False $ \a -> do
alive <- ioReactive $ readIORef aliveRef
when alive $ do
ioReactive $ writeIORef aliveRef False
scheduleLast $ ioReactive unlisten
push a
return unlisten
addCleanup_Listen unlistener l
split :: Event [a] -> Event a
split esa = Event gl cacheRef (dep esa)
where
cacheRef = unsafeNewIORef Nothing esa
gl = do
(l, push, nodeRef) <- ioReactive newEventImpl
unlistener <- later $ linkedListen esa (Just nodeRef) False $ \as ->
schedulePost $ map push as
addCleanup_Listen unlistener l
newBehavior :: a
-> Reactive (Behavior a, a -> Reactive ())
newBehavior = R.newBehavior
newBehaviour :: a
-> Reactive (Behavior a, a -> Reactive ())
newBehaviour = R.newBehaviour
mergeWith :: (a -> a -> a) -> Event a -> Event a -> Event a
mergeWith = R.mergeWith
filterE :: (a -> Bool) -> Event a -> Event a
filterE = R.filterE
gate :: Event a -> Behavior Bool -> Event a
gate = R.gate
collectE :: (a -> s -> (b, s)) -> s -> Event a -> Reactive (Event b)
collectE = R.collectE
collect :: (a -> s -> (b, s)) -> s -> Behavior a -> Reactive (Behavior b)
collect = R.collect
accum :: a -> Event (a -> a) -> Reactive (Behavior a)
accum = R.accum
class PriorityQueueable k where
priorityOf :: k -> IO Int64
newtype Sequence = Sequence Int64 deriving (Eq, Ord, Enum)
data PriorityQueue k v = PriorityQueue {
pqNextSeq :: IORef Sequence,
pqDirty :: IORef Bool,
pqQueue :: IORef (Map (Int64, Sequence) v),
pqData :: IORef (Map Sequence (k, v))
}
newPriorityQueue :: IO (PriorityQueue k v)
newPriorityQueue =
PriorityQueue <$> newIORef (Sequence 0) <*> newIORef False
<*> newIORef M.empty <*> newIORef M.empty
pushPriorityQueue :: PriorityQueueable k => PriorityQueue k v -> k -> v -> IO ()
pushPriorityQueue pq k v = do
prio <- priorityOf k
seq <- readIORef (pqNextSeq pq)
modifyIORef (pqNextSeq pq) succ
modifyIORef (pqQueue pq) (M.insert (prio, seq) v)
modifyIORef (pqData pq) (M.insert seq (k, v))
dirtyPriorityQueue :: PriorityQueue k v -> IO ()
dirtyPriorityQueue pq = writeIORef (pqDirty pq) True
popPriorityQueue :: PriorityQueueable k => PriorityQueue k v -> IO (Maybe v)
popPriorityQueue pq = do
maybeRegen
q <- readIORef (pqQueue pq)
if M.null q
then return Nothing
else do
let (pseq@(prio, seq), v) = M.findMin q
modifyIORef (pqQueue pq) (M.delete pseq)
modifyIORef (pqData pq) (M.delete seq)
return $ Just v
where
maybeRegen = do
dirty <- readIORef (pqDirty pq)
when dirty $ do
writeIORef (pqDirty pq) False
dat <- readIORef (pqData pq)
writeIORef (pqQueue pq) M.empty
forM_ (M.assocs dat) $ \(seq,(k,v)) -> do
prio <- priorityOf k
modifyIORef (pqQueue pq) (M.insert (prio, seq) v)
type ID = Int64
instance PriorityQueueable (Maybe (MVar Node)) where
priorityOf (Just nodeRef) = noRank <$> readMVar nodeRef
priorityOf Nothing = return maxBound
data ReactiveState = ReactiveState {
asQueue1 :: Seq (Reactive ()),
asQueue2 :: PriorityQueue (Maybe (MVar Node)) (Reactive ()),
asFinal :: Seq (Reactive ()),
asPost :: Seq (Reactive ())
}
instance Functor (R.Reactive Plain) where
fmap f rm = Reactive (fmap f (unReactive rm))
unReactive :: Reactive a -> StateT ReactiveState IO a
unReactive (Reactive m) = m
instance Applicative (R.Reactive Plain) where
pure a = Reactive $ return a
rf <*> rm = Reactive $ unReactive rf <*> unReactive rm
instance Monad (R.Reactive Plain) where
return a = Reactive $ return a
rma >>= kmb = Reactive $ do
a <- unReactive rma
unReactive (kmb a)
instance MonadFix (R.Reactive Plain) where
mfix f = Reactive $ mfix $ \a -> unReactive (f a)
ioReactive :: IO a -> Reactive a
ioReactive io = Reactive $ liftIO io
newtype NodeID = NodeID Int deriving (Eq, Ord, Enum)
data Partition = Partition {
paLock :: MVar (),
paNextNodeID :: IORef NodeID
}
scheduleEarly :: Reactive () -> Reactive ()
scheduleEarly task = Reactive $ modify $ \as -> as { asQueue1 = asQueue1 as |> task }
scheduleLast :: Reactive () -> Reactive ()
scheduleLast task = Reactive $ modify $ \as -> as { asFinal = asFinal as |> task }
schedulePost :: [Reactive ()] -> Reactive ()
schedulePost tasks = Reactive $ modify $ \as -> as { asPost = Seq.fromList tasks >< asPost as }
data Listen a = Listen { runListen_ :: Maybe (MVar Node) -> Bool -> (a -> Reactive ()) -> Reactive (IO ())
, listenerKeepAlive :: IORef ()
}
getListen :: Event a -> Reactive (Listen a)
getListen (Event getLRaw cacheRef _) = do
mL <- ioReactive $ readIORef cacheRef
case mL of
Just l -> return l
Nothing -> do
l <- getLRaw
ioReactive $ writeIORef cacheRef (Just l)
return l
linkedListen :: Event a -> Maybe (MVar Node) -> Bool -> (a -> Reactive ()) -> Reactive (IO ())
linkedListen ev mv suppressEarlierFirings handle = do
ioReactive $ evaluate ev
l <- getListen ev
unlisten <- runListen_ l mv suppressEarlierFirings handle
_ <- ioReactive $ touch l
return unlisten
listenTrans :: Event a -> (a -> Reactive ()) -> Reactive (IO ())
listenTrans ev handle = linkedListen ev Nothing False handle
data Observer p a = Observer {
obNextID :: ID,
obListeners :: Map ID (a -> Reactive ()),
obFirings :: [a]
}
data Node = Node {
noID :: NodeID,
noRank :: Int64,
noListeners :: Map ID (MVar Node)
}
newNode :: IO (MVar Node)
newNode = do
nodeID <- readIORef (paNextNodeID partition)
modifyIORef (paNextNodeID partition) succ
newMVar (Node nodeID 0 M.empty)
wrap :: (Maybe (MVar Node) -> Bool -> (a -> Reactive ()) -> Reactive (IO ())) -> IO (Listen a)
wrap l = Listen l <$> newIORef ()
touch :: a -> IO ()
touch a = evaluate a >> return ()
linkNode :: MVar Node -> ID -> MVar Node -> IO Bool
linkNode nodeRef iD mvTarget = do
no <- readMVar nodeRef
modified <- ensureBiggerThan S.empty mvTarget (noRank no)
modifyMVar_ nodeRef $ \no -> return $
let listeners' = M.insert iD mvTarget (noListeners no)
in listeners' `seq` no { noListeners = listeners' }
return modified
ensureBiggerThan :: Set NodeID -> MVar Node -> Int64 -> IO Bool
ensureBiggerThan visited nodeRef limit = do
no <- takeMVar nodeRef
if noRank no > limit || noID no `S.member` visited then do
putMVar nodeRef no
return False
else do
let newSerial = succ limit
putMVar nodeRef $ newSerial `seq` no { noRank = newSerial }
forM_ (M.elems . noListeners $ no) $ \mvTarget -> do
ensureBiggerThan (S.insert (noID no) visited) mvTarget newSerial
return True
unlinkNode :: MVar Node -> ID -> IO ()
unlinkNode nodeRef iD = do
modifyMVar_ nodeRef $ \no -> do
let listeners' = M.delete iD (noListeners no)
return $ listeners' `seq` no { noListeners = listeners' }
newtype Dep = Dep Any
dep :: a -> Dep
dep = Dep . unsafeCoerce
newEventImpl :: forall p a . IO (Listen a, a -> Reactive (), MVar Node)
newEventImpl = do
nodeRef <- newNode
mvObs <- newMVar (Observer 0 M.empty [])
cacheRef <- newIORef Nothing
rec
let l mMvTarget suppressEarlierFirings handle = do
(firings, unlisten, iD) <- ioReactive $ modifyMVar mvObs $ \ob -> do
let iD = obNextID ob
nextID' = succ iD
listeners' = M.insert iD handle (obListeners ob)
ob' = nextID' `seq` listeners' `seq`
ob { obNextID = nextID',
obListeners = listeners' }
unlisten = do
modifyMVar_ mvObs $ \ob -> do
let listeners' = M.delete iD (obListeners ob)
return $ listeners' `seq` ob { obListeners = listeners' }
unlinkNode nodeRef iD
return ()
return (ob', (reverse . obFirings $ ob, unlisten, iD))
modified <- case mMvTarget of
Just mvTarget -> ioReactive $ linkNode nodeRef iD mvTarget
Nothing -> return False
when modified $ dirtyPrioritized
unless suppressEarlierFirings $ mapM_ handle firings
return unlisten
listen <- wrap l
let push a = do
ioReactive $ evaluate a
ob <- ioReactive $ modifyMVar mvObs $ \ob -> return $
(ob { obFirings = a : obFirings ob }, ob)
when (null (obFirings ob)) $ scheduleLast $ ioReactive $ do
modifyMVar_ mvObs $ \ob -> return $ ob { obFirings = [] }
mapM_ ($ a) (M.elems . obListeners $ ob)
return (listen, push, nodeRef)
newEventLinked :: Dep -> IO (Event a, a -> Reactive (), MVar Node)
newEventLinked d = do
(listen, push, nodeRef) <- newEventImpl
cacheRef <- newIORef Nothing
let ev = Event {
getListenRaw = return listen,
evCacheRef = cacheRef,
eDep = d
}
return (ev, push, nodeRef)
instance Functor (R.Event Plain) where
f `fmap` e = Event getListen' cacheRef (dep e)
where
cacheRef = unsafeNewIORef Nothing e
getListen' =
return $ Listen (\mNodeRef suppressEarlierFirings handle -> do
linkedListen e mNodeRef suppressEarlierFirings (handle . f)) undefined
instance Functor (R.Behavior Plain) where
f `fmap` Behavior e s =
fs `seq` fe `seq` Behavior fe fs
where
fe = f `fmap` e
s' = unSample s
fs = s' `seq` Sample (f `fmap` s') (dep s) Nothing
constant :: a -> Behavior a
constant a = Behavior {
updates_ = never,
sampleImpl = Sample (return a) undefined Nothing
}
data BehaviorState a = BehaviorState {
bsCurrent :: a,
bsUpdate :: Maybe a
}
finalizeEvent :: Event a -> IO () -> Event a
finalizeEvent ea unlisten = ea { getListenRaw = gl }
where
gl = do
l <- getListen ea
ioReactive $ finalizeListen l unlisten
finalizeListen :: Listen a -> IO () -> IO (Listen a)
finalizeListen l unlisten = do
mkWeakIORef (listenerKeepAlive l) unlisten
return l
finalizeSample :: Sample a -> IO () -> IO (Sample a)
finalizeSample s unlisten = case sampleKeepAlive s of
Just keepaliveRef -> do
mkWeakIORef keepaliveRef unlisten
return s
Nothing -> error "finalizeSample called on sample with no keepAlive"
newtype Unlistener = Unlistener (MVar (Maybe (IO ())))
later :: Reactive (IO ()) -> Reactive Unlistener
later doListen = do
unlistener@(Unlistener ref) <- newUnlistener
scheduleEarly $ do
mOldUnlisten <- ioReactive $ takeMVar ref
case mOldUnlisten of
Just _ -> do
unlisten <- doListen
ioReactive $ putMVar ref (Just unlisten)
Nothing -> ioReactive $ putMVar ref mOldUnlisten
return unlistener
where
newUnlistener :: Reactive Unlistener
newUnlistener = Unlistener <$> ioReactive (newMVar (Just $ return ()))
addCleanup_Listen :: Unlistener -> Listen a -> Reactive (Listen a)
addCleanup_Listen (Unlistener ref) l = ioReactive $ finalizeListen l $ do
mUnlisten <- takeMVar ref
fromMaybe (return ()) mUnlisten
putMVar ref Nothing
addCleanup_Sample :: Unlistener -> Sample a -> IO (Sample a)
addCleanup_Sample (Unlistener ref) s = finalizeSample s $ do
mUnlisten <- takeMVar ref
fromMaybe (return ()) mUnlisten
putMVar ref Nothing
listenValueRaw :: Behavior a -> Maybe (MVar Node) -> Bool -> (a -> Reactive ()) -> Reactive (IO ())
listenValueRaw ba = lastFiringOnly $ \mNodeRef suppressEarlierFirings handle -> do
a <- sample ba
handle a
linkedListen (updates ba) mNodeRef suppressEarlierFirings handle
schedulePrioritized :: Maybe (MVar Node)
-> Reactive ()
-> Reactive ()
schedulePrioritized mNodeRef task = Reactive $ do
q <- gets asQueue2
lift $ pushPriorityQueue q mNodeRef task
dirtyPrioritized :: Reactive ()
dirtyPrioritized = Reactive $ do
q <- gets asQueue2
lift $ dirtyPriorityQueue q
lastFiringOnly :: (Maybe (MVar Node) -> Bool -> (a -> Reactive ()) -> Reactive (IO ()))
-> Maybe (MVar Node) -> Bool -> (a -> Reactive ()) -> Reactive (IO ())
lastFiringOnly listen mNodeRef suppressEarlierFirings handle = do
aRef <- ioReactive $ newIORef Nothing
listen mNodeRef suppressEarlierFirings $ \a -> do
ma <- ioReactive $ readIORef aRef
ioReactive $ writeIORef aRef (Just a)
when (isNothing ma) $ schedulePrioritized mNodeRef $ do
Just a <- ioReactive $ readIORef aRef
ioReactive $ writeIORef aRef Nothing
handle a
eventify :: (Maybe (MVar Node) -> Bool -> (a -> Reactive ()) -> Reactive (IO ())) -> Dep -> Event a
eventify listen d = Event gl cacheRef d
where
cacheRef = unsafeNewIORef Nothing listen
gl = do
(l, push, nodeRef) <- ioReactive newEventImpl
unlistener <- later $ listen (Just nodeRef) False push
addCleanup_Listen unlistener l
instance Applicative (R.Behavior Plain) where
pure = constant
b1@(Behavior e1 s1) <*> b2@(Behavior e2 s2) = Behavior u s
where
cacheRef = unsafeNewIORef Nothing s2
keepaliveRef = unsafeNewIORef () s2
u = Event gl cacheRef (dep (e1,e2))
s1' = unSample s1
s2' = unSample s2
gl = do
fRef <- ioReactive $ newIORef =<< unSample s1
aRef <- ioReactive $ newIORef =<< unSample s2
(l, push, nodeRef) <- ioReactive newEventImpl
unlistener <- later $ do
un1 <- linkedListen e1 (Just nodeRef) False $ \f -> do
ioReactive $ writeIORef fRef f
a <- ioReactive $ readIORef aRef
push (f a)
un2 <- linkedListen e2 (Just nodeRef) False $ \a -> do
f <- ioReactive $ readIORef fRef
ioReactive $ writeIORef aRef a
push (f a)
return (un1 >> un2)
addCleanup_Listen unlistener l
s = s1' `seq` s2' `seq` Sample (($) <$> s1' <*> s2') (dep (s1, s2)) (Just keepaliveRef)
changes :: Behavior a -> Event a
changes = updates
values :: Behavior a -> Event a
values = value
snapshotWith :: (a -> b -> c) -> Event a -> Behavior b -> Event c
snapshotWith = snapshot
count :: Event a -> Reactive (Behavior Int)
count = accum 0 . (const (1+) <$>)