heinrichapfelmus / reactive-banana Goto Github PK
View Code? Open in Web Editor NEWLibrary for functional reactive programming in Haskell.
Home Page: https://wiki.haskell.org/Reactive-banana
Library for functional reactive programming in Haskell.
Home Page: https://wiki.haskell.org/Reactive-banana
Splitting this off from issue #25 to stop derailing it :)
The basic idea is to add a combinator
delay :: Event a -> Event a
We define its semantics by adding an infinitesimal element ε to Time
and implementing delay
as follows:
delay = map (\(t,x) -> (t+ε,x))
We can then give a more natural definition for union
, avoiding the problems of simultaneous events (as discussed in issue #25):
union xss@((tx,x):xs) yss@((ty,y):ys) =
case compare tx ty of
LT -> (tx,x) : union xs yss
EQ -> (tx,x) : union (delay xs) (delay yss)
GT -> (ty,y) : union xss ys
and perhaps other combinators (stepper
?).
The complications of simultaneous occurrences can be eliminated without sacrificing the "compositionality" that union
offers is retained.
In fact, the more that I think about it, the more that it would be nice to have a way to delay an event. There is a situation in my code where the most declarative way for me to express my code unfortunately involved an event cycle which meant I had to uglify it and duplicate some code in order to break the cycle. Having the ability to delay an event in order to explicitly break cycles would be useful --- though it also gives one the ability to create infinite loops, as I had done inadvertently in my original definition :-), though I think I know how to fix that.
— @gcross
Time
seems simple to me.)I tried installing reactive-banana-wx-0.7.0.0 with examples from cabal:
cabal install reactive-banana-wx -fbuildExamples
But the CRUD module wouldn't build because it imports Tidings and there's no Tidings module visible.
reactive-banana-0.7.0.1 is already installed on my system.
I'm using ghc version 7.4.1
Here's the log:
Preprocessing executable 'CRUD' for reactive-banana-wx-0.7.0.0...
src/CRUD.hs:11:14:
Warning: -XRecursiveDo is deprecated: use -XDoRec or pragma {-# LANGUAGE DoRec #-} instead
src/CRUD.hs:26:8:
Could not find module `Tidings'
Use -v to see a list of the files searched for.
cabal: Error: some packages failed to install:
There's no Tidings.hs file in the reactive-banana-wx tarball that's posted on hackage.
Hey!
This is related to my previous posting, but the issue is sufficiently different I decided to create a new issue.
Suppose we have the following:
behavior :: Behavior (a -> Either b c)
initial_event :: Event a
left_event :: Event b
right_event :: Event c
In issue #17 I mentioned that there is not a clean way to say that left_event fires when initial_event fires and behavior is Left. However, another problem is that it seems both inefficient and possibly dangerous to define left_event and right_event independently because not only does the system have to execute what is essentially the same check twice, but the value of the behavior might change after executing, say, left_event, which causes right_even to fire in addition to left_event when this was not wanted.
I would that this would be a whole lot trickier to implement than the functions I suggested in #17, but it would nonetheless be handy for there to be a function something like
switchOnBehavour :: Behavior (a -> Either b c) -> Event a -> (Event b, Event c)
or alternatively (and presumably equivalently)
switch :: Event (Either a b) -> (Event a, Event b)
The semantics functions would include a promise that one and only one of the events in (Event b, Event c) will be fired in response to an input event.
Is it possible to write a version of https://github.com/HeinrichApfelmus/reactive-banana/blob/master/reactive-banana-wx/src/Counter.hs, de-coupled from wx? It looks like a very useful basic example. A similar in nature ActuatePause.hs example provides an increasing counter example, while a *wx example provides increasing and decreasing version of it.
At the moment it requires digging through framework related code in order to get the basics out. It is likely that having an example, de-coupled from any particular GUI framework, will be useful.
Thanks,
Vlad
Since GHC 7.4.1 Eq
and Show
are no longer superclasses of Num
.
Hello Heinrich,
I am stuck trying to install reactive-banana, both from source and using cabal install
:
$ cabal configure && cabal build
Resolving dependencies...
Configuring reactive-banana-0.4.1.1...
Warning: Unknown extensions: TupleSections
Warning: This package indirectly depends on multiple versions of the same
package. This is highly likely to cause a compile failure.
package template-haskell-2.4.0.1 requires containers-0.3.0.0
package hpc-0.5.0.5 requires containers-0.3.0.0
package ghc-binary-0.5.0.2 requires containers-0.3.0.0
package ghc-6.12.3 requires containers-0.3.0.0
package Cabal-1.8.0.6 requires containers-0.3.0.0
package reactive-banana-0.4.1.1 requires containers-0.4.0.0
Preprocessing library reactive-banana-0.4.1.1...
Building reactive-banana-0.4.1.1...
[3 of 7] Compiling Reactive.Banana.PushIO ( src/Reactive/Banana/PushIO.hs, dist/build/Reactive/Banana/PushIO.o )
src/Reactive/Banana/PushIO.hs:164:32: Not in scope: `void'
% cabal install reactive-banana
Resolving dependencies...
cabal: dependencies conflict: ghc-6.12.3 requires containers ==0.3.0.0 however
containers-0.3.0.0 was excluded because containers-0.4.0.0 was selected
instead
containers-0.3.0.0 was excluded because reactive-banana-0.4.1.1 requires
containers ==0.4.*
Not sure what's going on here either way -- do I need to install something that hasn't been given as an explicit dependency, or is reactive-banana not compatible with GHC 6.12? I'd greatly appreciate any help.
It seems, that event0
produces occasional crashes in Windows, below is description and an example.
There are three possible behaviours I observed:
error message is
Segmentation fault/access violation in generated code
when amount of buttons (see below) is one, (1) never occurs
upon increase of amount of buttons, (2) seems to occure less, than before
amount of (1) or (2) is estimated to be about 30-40 %
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Monad (replicateM, mapM_, void)
import Graphics.UI.WX hiding (Event)
import Reactive.Banana
import Reactive.Banana.WX
main = start $ do
pad <- frame []
buttons <- replicateM 3 -- this amount influences on behaviour
$ button pad []
let network :: forall t. Frameworks t => Moment t ()
network = void $ mapM_ (`event0` command) buttons
-- ^ crash inside ^
net <- compile network
actuate net
When building from hackage I saw an error on the missing containers
like in the error log here:
http://hackage.haskell.org/packages/archive/reactive-banana-wx/0.4.1.1/logs/failure/ghc-7.0
In building from this repository, from the latest commit, I had to add array
(0.3.), containers
(0.4.), and process
(1.0.*) to the reactive-banana-wx.cabal.
Then things seem to work well: I'm looking at the demos now!
Cheers,
Kevin
It would be very nice if we could get rid of the Typeable
constraint on the fromAddHandler
function, so that its type signature becomes
fromAddHandler :: AddHandler a -> NetworkDescription (Event a)
Not sure if that is possible, but this issue will remind me to look for a solution.
Gregory Crosswhite writes that he'd like the following functions added to the API
filterJust :: Event (Maybe a) -> Event a
split :: Event (Either a b) -> (Event a, Event b)
newHandler :: NetworkDescription (Event a, a -> IO ())
I've already added filterJust
. The definitions for the other two are
split e = (fromLeft <$> filterE isLeft e, fromRight <$> filterE isRight e)
newHandler = do
(event_handler,callback) <- liftIO newAddHandler
event <- fromAddHandler event_handler
return (event,callback)
I'm happy to add them, probably with a different name.
I'm not too keen on newHandler
, because it's usually not very useful to have the callback function available inside a NetworkDescription
, but I can see why it might be useful.
There are some very useful tests in the Reactive.Banana.Test
module. However, I don't know how to make them work, as they rely on internal API; I have no idea how to specify their dependencies to this end.
Here is an example usage of a .cabal file with tests.
https://github.com/bos/statistics/blob/master/statistics.cabal#L216
Not sure whether this covers the internal use case, though.
First, I've not been able to move the rocket ship. I've fiddled around with the code but don't understand it well enough yet to fix things. I probably need to browse around the libraries and make some sort of keyboard event into an instance of Show so i can figure out what keys the thing thinks I'm pressing...
Second, although the Game -> Pause option works, but if I select the Game -> New option (or press CTRL-N) it crashes with the error: Asteroids: Maybe.fromJust: Nothing
. I know this is an error that can be thrown by fromJust Nothing
. I'm not at all sure, but it seems like this is something lurking in PushIO.hs related to what happens as the asteroids
command is run again within the network.
Cheers,
Kevin
Hi!
I was playing around with your library, and something that caused me difficulty was that there were many times when I essentially wanted to "case of" on a Behavior but didn't see a clean way of doing this.
To be more concrete, suppose that we have the following:
b :: Behavior (Maybe Int)
e1 :: Event ()
e2a :: Event Int
e2b :: Event ()
Now suppose that when e1 fires I want to fire e2a if b is (Just Int) and e2b is b is (Nothing) --- for example, e1 could be a request for the value in b, e2a could be a success and e2b a failure. Unfortunately, there is not a good operation in reactive-banana that lets me express these semantics. The best I can see would be to write something like the following:
e2a = apply (fmap (const . fromJust) b) . filterApply (fmap (const . isJust) b) $ e1
However, there are two thing that bug me about this solution. First, it is not clear to me that this is guaranteed to work all the time because it isn't clear to me that the semantics of the network allow me to be sure that the value of b hasn't changed between the isJust and the fromJust --- say, in a concurrent setting.
Second, even if that were not an issue, this type of code is fundamentally unsafe because of its use of fromJust; while it is obviously fine in this context, one could imagine more complicated contexts where the filter and the extractor were sufficiently separated that it might not be immediately apparent that making a filter more permissive in one part of the code breaks another part of the code.
There are two possible operations you could supply that could fix all of this. First, the following:
filterApplyMaybe :: Behavior (a -> Maybe b) -> Event a -> Event b
The semantics of this function are that the function in the behavior is applied to events; if the result is Maybe then the event is discarded, and otherwise the x is extracted from (Just x) and returned inside the resulting event.
Alternatively, one could supply a function of the form:
filterMaybe :: Event (Maybe a) -> Event a
The semantics of this function are that events holding Nothing are filtered whereas those holding (Just x) are allowed to remain with the x being extracted out.
Anyway, you were interested in feedback from people trying to use your library to solve problems so I am just supplying my own. :-)
Both the CurrencyConverter and Arithmetic examples use this function, which used to be defined like so, but which is no longer around:
behaviorText
:: WX.TextCtrl w
-> String
-- ^ Initial value supplied "by the user". Not set programmaticaly.
-> NetworkDescription t (Behavior t String)
behaviorText textCtrl initial = do
-- Should probably be wxEVT_COMMAND_TEXT_UPDATED ,
-- but that's missing from wxHaskell.
addHandler <- liftIO $ event1ToAddHandler textCtrl keyboardUp
e <- fromAddHandler $ mapAddHandlerIO (const $ get textCtrl text) addHandler
return $ stepper initial e
Looking at the stuff you have elsewhere, this is probably going to be an easy fix, but I'm not sure I'll get around to this quickly myself and don't want to forget...
Anyways, it is nice to see this stuff coming along. :)
reactive-banana makes heavy use of extensions (GADTs, type families), preventing it from being compiled with the UHC compiler.
The semantics model itself (model.hs) is clean in this respect.
While the model isn't meant for high-performance situations, it probably suffices for web applications (UHC's javascript target).
It would be very nice if reactive-banana could have a smaller brother (reactive-banana-light) with the same semantics/API.
This way, code will be fairly portable between the light and normal versions.
My cabal install of reactive-banana-wx doesn't have a banana.png file.
I installed reactive-banana-wx like:
cabal install reactive-banana-wx -fbuildExamples
The Animation executable wants it here: .cabal/share/reactive-banana-wx-0.7.0.0/banana.png
The type of union
is
union :: FRP f => Event f a -> Event f a -> Event f a
It would be convenient if it were instead
union :: FRP f => Event f a -> Event f b -> Event f (Either a b)
Rationale: Say I have
reactimate $ mainQuit <$ eQuit
and I want to build eQuit up from the main window closing, Ctrl+Q being pressed, or SIGINT being received. The obvious definition is
let eQuit = eWindowClose `union` eCtrlQ `union` eSIGINT
Unfortunately, this doesn't work, because eWindowClose is of type Event WindowEvent
, eCtrlQ is of type Event KeyPress
, and eSIGINT is of type Event SignalInfo
! So instead I must do
let eQuit = (() <$ eWindowClose) `union` (() <$ eCtrlQ) `union` (() <$ eSIGINT)
which is really ugly.
Another comparison: To implement the current behaviour of union
with this new type, all that has to be done is:
let oldUnion a b = either id id (union a b)
(or oldUnion = either id id .: union
). To implement the behaviour of this new type with the current union
is less elegant:
let newUnion a b = union (Left <$> a) (Right <$> b)
If union
's type is changed, then it may be worth adding an obvious dual function intersectE :: Event f a -> Event f b -> Event f (a,b)
. I'm not sure how useful this would be, though; I don't think it can be implemented with the current model.
At the moment, it is impossible to call an event handler from inside a reactimate
call, for example like this:
do
(addHandler1, _) <- newAddHandler
(addHandler2, fire2) <- newAddHandler
network <- compile $ do
e1 <- fromAddHandler addHandler1 :: NetworkDescription (Event ())
e2 <- fromAddHandler addHandler2
reactimate $ fire2 () <@ e1 -- doesn't work
actuate network
This is correct because a naïve implementation might corrupt internal state, that's why event handling is performed atomically.
The issue is that situations like this actually happens in practice! Namely in the CRUD example in reactive-banana-wx. If the focus is on one of the entry fields and you press the delete button, then wxWidgets will eventually generate a "lose focus" event for the entry fields, which is passed to the event network. But this happens while the event network is still processing the "delete button" event and the network will (rightfully) hang.
A better solution would be to queue incoming events and fire them once the previous event has been handled completely. I have to think about whether this solution is correct, though, but it certainly seems to be desirable. (Note: Something similar will have to be done anyway when I add proper support for time in the semantics.)
I've run into a memory leak exemplified by the following code, which fills up about 100MB/sec. The key factors seem to be:
foo
that is firing rapidlybar
that is constructed from apply
; it makes no difference whether bar
is firing or not, or whether bar
is in any way related to foo
.I haven't been able to figure much out from profiling. The allocations seem to be associated with dozens of different cost centers.
I am using GHC 7.6.1 and reactive-banana 0.7.1.1.
{-# LANGUAGE ScopedTypeVariables #-}
module Main where
import Reactive.Banana
import Reactive.Banana.Frameworks
networkDescription :: forall t. (Frameworks t) => AddHandler () -> Moment t ()
networkDescription addHandler = do
foo <- fromAddHandler addHandler
let bar = (pure (const True) :: Behavior t (()->Bool)) `apply` foo
--let bar = (pure (const True) :: Behavior t (()->Bool)) `apply` never :: Event t Bool -- this line has the same problem
--let bar = (const True) `fmap` foo -- but this line fixes the problem
-- commenting out either reactimate also fixes the problem
reactimate $ fmap (const $ return()) foo
reactimate $ fmap (const $ return ()) bar
main::IO()
main = do
(addHandler,runHandlers) <- newAddHandler
network <- compile (networkDescription addHandler)
actuate $ network
let loop = do
runHandlers ()
loop
loop
I would like to add a type parameter to the Event
, Behavior
and NetworkDescription
types in preparation for the implementation of dynamic event switching. In other words, functions will now like this:
filterE :: (a -> Bool) -> Event t a -> Event t a
fromAddHandler :: AddHandler a -> NetworkDescription t (Event t a)
In return, the FRP
is going to be removed, so that's one parameter less to worry about. (You will still be able to check event graphs against the model implementation.)
More details on my blog.
One of the "small efficiency problems" I alluded to in the package description is that behaviors are sometimes recomputed unnecessarily.
Example:
let b = bexpensive <*> bin
in (liftA2 (+) b b) <@> e1
The value corresponding to bin
will be computed twice. While this doesn't make a difference semantically, it may be expensive.
There is no fundamental reason for this omission, I was just too lazy to implement it. The main reason for that is that I will have to revamp all the internals in a future release anyway. The future release won't happen very soon, though, that's why I'm mentioning the issue here.
By the way, the offending code is the ApplyB
case of the following function:
-- compile a behavior
-- FIXME: take care of sharing, caching
compileBehaviorEvaluation :: Behavior Linear a -> Run a
compileBehaviorEvaluation = goB
where
goB :: Behavior Linear a -> Run a
goB (ref, Pure x) = return x
goB (ref, ApplyB bf bx) = goB bf <*> goB bx
goB (ref, ReadBehavior refb) = readBehaviorRef refb
At the moment, it doesn't include any provisions for observable sharing.
Let me know if this turns out to be a problem for you; I will fix it if there is demand.
Here is test case. When I run code it hangs for a few seconds and then NonTermination
exception is thrown. Code is rather large but I cannot reduce it further.
module Main(main) where
import Data.IORef
import Reactive.Banana
import Reactive.Banana.Frameworks
-- | Visualize query result
main :: IO ()
-- main = runGuiInSubprocess $ do
main = do
ref <- newIORef undefined
net <- compile $ do
(initEvt,runInit) <- newEvent
let listWidget bhvXs = do
(cmdEvt,runCmd) <- newEvent
evtBhv <- changes bhvXs
let xsEvt = (bhvXs <@ initEvt) `union` evtBhv
getEvents = filterJust $ listEvents xsEvt cmdEvt
reactimate
$ (return () <$)
$ scanE (const Just) Nothing getEvents
return (getEvents,runCmd)
(e,run) <- listWidget $ pure [10 .. 20 :: Int]
_ <- listWidget $ stepper [] $ fmap (\n -> [0..n]) e
liftIONow $ writeIORef ref (runInit,run)
--
(runInit,run) <- readIORef ref
actuate net
runInit ()
run ()
----------------------------------------------------------------
data Cursor a
= Cursor [a] Int
| Invalid
listEvents :: Event t [a] -> Event t () -> Event t (Maybe Int)
listEvents listEvt command
= fmap fini
$ scanE acc Invalid
$ listEvt `joinE` command
where
--
fini (Cursor _ i) = Just i
fini Invalid = Nothing
-- Accumulate data
acc Invalid (Left []) = Invalid
acc Invalid (Left xs) = Cursor xs 0
acc Invalid _ = Invalid
acc (Cursor _ n) (Left xs) = Cursor xs n
acc (Cursor xs n) (Right ()) = Cursor xs (n + 1)
scanE :: (a -> b -> a) -> a -> Event t b -> Event t a
scanE fold x0 = accumE x0 . fmap (flip fold)
joinE :: Event t a -> Event t b -> Event t (Either a b)
joinE ea eb = (Left <$> ea) `union` (Right <$> eb)
(This issue was kindly brought to my attention by Andrew Richards.)
Consider the following program:
import Reactive.Banana
import Debug.Trace
main = do
(inputAH, inputTrigger) <- newAddHandler
network <- compile $ do
evt <- fromAddHandler inputAH
let g = stepperD 0 evt
let out = f <$> g <*> g
reactimate $ print <$> changes out
actuate network
inputTrigger 3
inputTrigger 4
f :: Int -> Int -> Int
f x y = trace ("f: " ++ show (x, y)) $ x + y
The function f
is called more than once per update of the single input, even though it is unnecessary to call it more than once. Is it possible to optimize the Discrete
type to avoid this?
Most likely yes, but I don't know how to perform this kind of optimization in the push-based implementation I currently have (version 0.4).
Basically, Discrete
is implemented as a pair
type Discrete a = (Behavior a , Event a).
of a behavior (for recursion) and an update event. By duplicating g
, the update event is also duplicated, resulting in two simultaneous update event occurrences for the event out
. What's currently missing from reactive-banana is a combinator
calm :: Event a -> Event a
that removes all but the last event occurrence from a bunch of simultaneous events. This would allow us to optimize the Discrete data type. The combinator seems to be safe from a semantic point of view.
Consider the following example:
import Data.IORef
import Reactive.Banana
import Reactive.Banana.Frameworks
type EventSource a = (AddHandler a, a -> IO ())
addHandler :: EventSource a -> AddHandler a
addHandler = fst
fire :: EventSource a -> a -> IO ()
fire = snd
main :: IO ()
main = do
source <- newAddHandler
ref <- newIORef True
network <- compile $ do
bRef <- fromPoll (readIORef ref)
e <- fromAddHandler (addHandler source)
reactimate $ print <$> bRef <@ e
actuate network
writeIORef ref False
fire source ()
fire source ()
Running this using reactive-banana 0.7.1.1, I get the following output:
> :main
True
False
According to the documentation of fromPoll, "the resulting Behavior will be updated on whenever the event network processes an input event", but it seems to be the case that the bRef behavior is not updated, since the example prints True on the first firing of source.
Is this the correct behavior? The result was surprising to me, as ref is written before any events are fired, but I might have misunderstood the semantics of the library. I would have expected the following output:
> :main
False
False
Thanks!
In
{-# LANGUAGE Rank2Types #-}
import Reactive.Banana
import Reactive.Banana.Frameworks
main = return ()
f, f' :: (forall t. Frameworks t => Behavior t (() -> ())) -> IO EventNetwork
f b = compile $ newEvent >>= reactimate . fmap return . apply b . fst
f' b = compile $ newEvent >>= reactimate . fmap return . (b <@>) . fst
the definition of f
does not produce any errors, while the definition of f'
produces the following errors due to the use of (<@>)
:
[1 of 1] Compiling Main ( Main.hs, Main.o )
Main.hs:10:59:
Could not deduce (Frameworks t0) arising from a use of `b'
from the context (Frameworks t)
bound by a type expected by the context:
Frameworks t => Moment t ()
at Main.hs:10:8-70
The type variable `t0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there is a potential instance available:
instance Frameworks
(reactive-banana-0.7.1.1:Reactive.Banana.Internal.Phantom.FrameworksD,
t)
-- Defined in `reactive-banana-0.7.1.1:Reactive.Banana.Internal.Phantom'
In the first argument of `(<@>)', namely `b'
In the first argument of `(.)', namely `(b <@>)'
In the second argument of `(.)', namely `(b <@>) . fst'
Main.hs:10:61:
Could not deduce (Apply (Behavior t0) (Event t))
arising from a use of `<@>'
from the context (Frameworks t)
bound by a type expected by the context:
Frameworks t => Moment t ()
at Main.hs:10:8-70
The type variable `t0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there is a potential instance available:
instance Apply (Behavior t) (Event t)
-- Defined in `Reactive.Banana.Combinators'
Possible fix:
add an instance declaration for (Apply (Behavior t0) (Event t))
In the first argument of `(.)', namely `(b <@>)'
In the second argument of `(.)', namely `(b <@>) . fst'
In the second argument of `(.)', namely
`fmap return . (b <@>) . fst'
OK, this is probably a fairly controversial proposal, so I ask you to hear it out until the end before forming an opinion :)
Behavior and Discrete essentially represent the same thing: a time-varying value. I find the documentation's claim that Discrete is somehow inherently discrete dubious; apart from changes
, the interface of Discretes is identical to that of Behaviors. Which to choose basically comes down to
In my opinion, these concerns and their manifestation as Behavior vs. Discrete collide to make code less declarative, harder to structure, and harder to understand. So I went through working code using reactive-banana: the reactive-banana-wx examples.
In Arithmetic.hs, Behaviors are only used to poll the state of a text box in reaction to events.
In Asteroids.hs, Behaviors are used for the game state. bship
does not use Behaviors from the outside world, and I think it could be made Discrete without losing anything. brocks
uses a brandom
Behavior to poll the RNG from the outside world. However, it is only consulted in response to the etick
Event. (Incidentally, I'm not sure an RNG is a proper Behavior like this; the act of observing it changes it, and nothing else does (barring other threads accessing the global RNG too). That seems wrong to me.)
In CRUD.hs, Behavior is used to construct the DatabaseTime
record, which has an associated comment noting its similarity to Discrete. I believe DatabaseTime
could easily be rewritten in terms of Discrete. It is also used to poll the state of a text box.
In Counter.hs, no Behaviors are used.
In CurrencyConverter.hs, a single Behavior is used to poll the state of a text box in response to an Event.
In NetMonitor.hs, a Behavior is used to poll the state of the network in response to an Event.
In TicTacToe.hs, no Behaviors are used.
In TwoCounter.hs, a single Behavior is used to track the state of a toggle; it could easily be a Discrete instead.
In Wave.hs, no Behaviors are used.
Next I glanced at BlackBoard, which I could only find in the tree of commit b8c353b. It was too large for me to give it a really in-depth examination, but no use of Behaviors that don't fit in one of the above categories (poll cheap state, poll expensive state based on an event, or could easily be Discrete) immediately jumped out to me.
I took a brief glance at the only other public project using reactive-banana I know of, Jaek, but it was far too large for me to form an accurate impression. I suspect the majority or all of its Behaviors fit in one of the aforementioned categories too, though.
Now I'll address each category that I've shown Behaviors to be used for separately:
fromPoll :: Event (IO a) -> NetworkDescription (Event a)
. You could, for instance, write brandom <- fromPoll $ (randomRIO (0,1) :: IO Double) <$ etick
, and then construct a Discrete
based on this. A useful combinator might be fromPollD :: Discrete (IO a) -> NetworkDescription (Discrete a)
. This does reduce modularity to a degree, but from the POV of interfacing with the real world, it's clearer (you control exactly when the possibly-expensive polls will occur), and I think such uses of Behavior are fairly rare anyway.So, why use Behaviors instead of Discrete? Well, an obvious reason is that changes
exposes more than the abstract interface of a Behavior does. (One could make the same argument about initial
, but I don't think there's any reason not to offer this function for Behaviors, other than that as an "implementation detail", the computation of a Behavior can cause side-effects in reactive-banana.)
So here's my proposal:
fromPoll
with the form I suggested above.initial
and changes
as follows:initial :: Behavior a -> NetworkDescription a
changes :: Behavior a -> NetworkDescription (Event a)
changes
) as being intended solely for the use of interfacing to the outside world.Note that I'm pretty sure that none of the above would involve any changes to the model; Discrete is a perfectly cromulent implementation of the Behavior type family.
Thanks for reading, and I'm interested in hearing your thoughts on this (for any value of you — if you're reading this bug report and use reactive-banana, please speak up!) :)
I propose to add MonadIO instance to Moment data type with liftIO = liftIONow
. NetworkDescription had such instance.
Reid Barton reports:
By the way, I also encountered a simple bug involving spill
: trying to spill an empty list causes a "head []" type of exception. I've worked around it by using
spill' = R.spill . R.filterE (not . null)
but I imagine that there might be a better implementation in terms of the internal primitives.
The documentation states that there is instance Monoid (Event t (a -> a))
, but you commented it out.
Documentation doesn't say anything about what happens when exception is thrown in event handler bound by reactimate
. Apprently thread with event network is killed. Another problem is that there is no reliable way to signal to main loop that network is dead.
Behaviors/Events that recursively depend on themselves result in a <> exception in the implementation, even though that model behaves correctly.
I have pasted a minimal implementation exhibiting this bug at: https://gist.github.com/merijn/6049162
To switch between the model and real implementation to see the bug:
Given issue #4, should union
be changed to unionE
to avoid clashes with a standard Data.List function?
It seems that src/Reactive/Banana/Prim/Dependencies.hs:22
is hiding an empty
from the Order module that doesn't even exist. Thereby, it produces an error while (cabal) installing it. After removing it, the installation process succeeds.
Maybe use just banana
, to differentiate it from the already existing reactive
library?
Also, namespace pollution. How about Reactive.Banana.Cor
etc.
The following code demonstrates a difference between the PushIO and Model implementations:
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Maybe
import qualified Reactive.Banana.Implementation as PushIO
import Reactive.Banana.Incremental
import Reactive.Banana.Model as Model
data Incoming = IncomingA | IncomingB deriving (Show)
data Outgoing = Outgoing deriving (Show)
filterJust :: FRP f => Event f (Maybe a) -> Event f a
filterJust = fmap fromJust . filterE isJust
f :: forall f. FRP f => Event f Incoming -> Event f Outgoing
f incoming = outgoing
where
incomingA :: Event f ()
incomingA =
filterJust
$
(\x -> case x of
IncomingA -> Just ()
_ -> Nothing
)
<$>
incoming
incomingB :: Event f ()
incomingB =
filterJust
$
(\x -> case x of
IncomingB -> Just ()
_ -> Nothing
)
<$>
incoming
discrete = accumD () (id <$ incomingB)
outgoing = Outgoing <$ (changes discrete)
main1 = return (Model.interpret f [IncomingA,IncomingB]) >>= print
main2 = (PushIO.interpret f [IncomingA,IncomingB]) >>= print
-- Output: Prelude.last: empty list
-- main = main1
-- Output: [[],[Outgoing]]
-- main = main2
When the Model implementation is used, the program crashes. When the PushIO implementation is used, it runs just fine and produces the correct answer.
Allow reactive-banana-wx to use both wxWidgets 0.13 and 0.90.
Older versions had a correct list of dependencies
http://hackage.haskell.org/package/reactive-banana-wx-0.6.0.1
Also include the *.png
file.
Also works with base 4.3.* .
Module Reactive.Banana exports filter which clashes with filter from Prelude. This forces user either to import r.banana qualified no other function clashes or hide filter from Prelude which is not convenient. Maybe it's worth to rename filter to filterE (like accumE)?
Several proposals for changes in semantics are floating around right now
The cost of switching semantics in an existing library are traditionally high, but I want to facilitate experimentation with the following proposal.
Implement a primitive version of the API on top of which alternate proposals can be implemented.
The idea is to have a module Primitive
offering, say, two data types
module Reactive.Banana.Primitive where
data Event a
data Behavior a
which are the basic building blocks for implementing other semantics. (Though I'm not sure yet what these data types are going to be.)
For instance, event streams that allow multiple simultaneous events (see issue #22) can then be implemented as
module Reactive.Banana.Simultaneous where
import qualified Reactive.Banana.Primitive as Primitive
type Event a = Primitive.Event [a]
type Behavior a = Primitive.Behavior a
union :: Event a -> Event a -> Event a
union = Primitive.unionWith (++)
...
Another example would be behaviors with an integrated notion of updates (issue #25)
module Reactive.Banana.Updated where
import qualified Reactive.Banana.Primitive as Primitive
type Event a = Primitive.Event a
type Behavior a = (Primitive.Behavior a, Event a)
fromPoll :: IO a -> Event () -> NetworkDescription (Behavior a)
fromPoll m e = do
b <- Primitive.fromPoll m
return $ (b, b <@ e)
...
And so on.
Of course, one variant would count as "offical" and the intention is that experimentation will help find the most blessed semantics. This proposal also allows for reasonably easy backward compatibility whenever the official semantics are being changed; you just have to change a few imports.
Thoughts?
Should be fromRight
instead of fromright
in the definition of split
.
I'm seeing errors when trying to install reactive-banana-wx
. From a look at the Setup.hs, I'm not sure why this should even matter on Ubuntu, but this is what happens:
[1 of 1] Compiling Main ( /tmp/reactive-banana-wx-0.4.1.118614/reactive-banana-wx-0.4.1.1/Setup.hs, /tmp/reactive-banana-wx-0.4.1.118614/reactive-banana-wx-0.4.1.1/dist/setup/Main.o )
/tmp/reactive-banana-wx-0.4.1.118614/reactive-banana-wx-0.4.1.1/Setup.hs:10:22:
Couldn't match expected type `Distribution.Simple.Setup.BuildFlags'
with actual type `Cabal-1.10.2.0:Distribution.Simple.Setup.BuildFlags'
Expected type: Args
-> Distribution.Simple.Setup.BuildFlags
-> Distribution.PackageDescription.PackageDescription
-> Distribution.Simple.LocalBuildInfo.LocalBuildInfo
-> IO ()
Actual type: Cabal-1.10.2.0:Distribution.Simple.UserHooks.Args
-> Cabal-1.10.2.0:Distribution.Simple.Setup.BuildFlags
-> Cabal-1.10.2.0:Distribution.PackageDescription.PackageDescription
-> Cabal-1.10.2.0:Distribution.Simple.LocalBuildInfo.LocalBuildInfo
-> IO ()
In the return type of a call of `appBundleBuildHook'
In the `postBuild' field of a record
cabal: Error: some packages failed to install:
reactive-banana-wx-0.4.1.1 failed during the configure step. The exception
was:
ExitFailure 1
This is on the Ubuntu Oneiric beta with:
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.0.3
$ cabal --version
cabal-install version 0.10.2
using version 1.10.1.0 of the Cabal library
In Reactive.Banana.Internal.PulseLatch0
you are using unsafePerformIO
several times without using a NOINLINE
pragma. Is this intentional or could problems as described in the documentation arise?
When merging two event stream, what should happen to simultaneous event occurrences? As of reactive-banana version 0.4, they will be kept around and ordered from left to right. Here an example in terms of the model implementation:
union [[1],[2]], [[3],[]] = [[1,3], [2]]
I propose to dispense with these kind of simultaneous event occurrences and change the Event
data type so that only a single event may happen at each point in time.
The type of union
will be changed to
union :: Monoid a => Event a -> Event a -> Event a
Instead of appending simultaneous events, the union
function would merge them into a single event occurrence, using the mappend
combinator.
Since a -> a
is a monoid, this works well in conjunction with the accumulation functions accumE
, accumB
. Existing code is essentially unchanged in this regard, except for ordering.
Merging events in other cases will be a little less pleasant, we probably need a left-biased union
lunion :: Event a -> Event a -> Event a
x `lunion` y = (filterJust . getFirst) (First x `mappend` First y)
The drawback is that event occurrences may be lost this way.
The following two behaviors are now equivalent
initial :: A
update :: A -> A
behavior1 :: Event () -> Behavior A
behavior1 e = accumB initial $ update <$ e
where event = update <$> (b1 <@ e)
behavior2 :: Event () -> Bevahior A
behavior2 e = stepper initial event
where event = accumE initial $ update <$ e
In the presence of simultaneous event occurrences, these two programs are not equivalent because the event e
may contain multiple occurrences.
To summarize 2 and 3: the trade-off is that we now have to think about simultaneous events whenever we take the union of two event streams, but we are freed from thinking about them when we take the snapshot of a behavior. Gregory Crosswhite reports that this would probably simplify a program he writes.
The problem about optimizing the Discrete
type (issue #19) disappears.
The old semantics can always be obtained as Event [a]
if desired. In fact, that's how the model implementation defines the current semantics. The calm
function mentioned in issue #19 becomes straightforward to implement.
What do you think? How would this change affect your existing code base?
Okay, so accumE's type is:
accumE :: a -> Event t (a -> a) -> Event t a
At first, second and third glance this is a type that introduces a sort of "WTF?????" expression on one's face. I was expecting something like
accumELeft :: b -> (a -> b -> b) -> Event t a -> Event t b
Which is simultaneously more general and less general.
Hi Heinrich!
My tutorial has progressed quite a bit. My biggest gripe is how hard it is to do things that aren't hooking it up to wxHaskell or something similar to wxHaskell. I should not have had to write this:
addHandlerFromThread :: (Chan a -> IO ()) -> AddHandler a
addHandlerFromThread writerThread handler = do
chan <- newChan
tId1 <- forkIO (writerThread chan)
tId2 <- forkIO $ forever $ (readChan chan >>= handler)
return (killThread tId1 >> killThread tId2)
And what if I have a thread or group of threads that consume multiple inputs and/or produce multiple outputs? I think I need a lower level interface for that i.e. either you or me should write a more general system for hooking stuff up to FRP-land.
Regards,
Echo
I think that says it all.
I tried to install reactive-banana-wx on OS X running GHC 7.6.3 (recent Haskell Platform). I have wx libraries (wx-0.90.0.1, wxc-0.90.0.4, wxcore-0.90.0.3, wxdirect-0.90.0.1) and cabal-macosx-0.2.2 and Cabal-1.16.0. When I try to install I get:
cabal install reactive-banana-wx
Resolving dependencies...
[1 of 1] Compiling Main ( /var/folders/_c/4n2x0zfx7mx5gk_46pdxn3pm0000gn/T/reactive-banana-wx-0.7.1.0-16274/reactive-banana-wx-0.7.1.0/Setup.hs, /var/folders/_c/4n2x0zfx7mx5gk_46pdxn3pm0000gn/T/reactive-banana-wx-0.7.1.0-16274/reactive-banana-wx-0.7.1.0/dist/setup/Main.o )
/var/folders/_c/4n2x0zfx7mx5gk_46pdxn3pm0000gn/T/reactive-banana-wx-0.7.1.0-16274/reactive-banana-wx-0.7.1.0/Setup.hs:10:22:
Couldn't match type Cabal-1.16.0.3:Distribution.Simple.Setup.BuildFlags' with
Distribution.Simple.Setup.BuildFlags'
Expected type: Args
-> Distribution.Simple.Setup.BuildFlags
-> Distribution.PackageDescription.PackageDescription
-> Distribution.Simple.LocalBuildInfo.LocalBuildInfo
-> IO ()
Actual type: Cabal-1.16.0.3:Distribution.Simple.UserHooks.Args
-> Cabal-1.16.0.3:Distribution.Simple.Setup.BuildFlags
-> Cabal-1.16.0.3:Distribution.PackageDescription.PackageDescription
-> Cabal-1.16.0.3:Distribution.Simple.LocalBuildInfo.LocalBuildInfo
-> IO ()
In the return type of a call of appBundleBuildHook' In the
postBuild' field of a record
In the second argument of ($)', namely
simpleUserHooks {postBuild = appBundleBuildHook guiApps}'
Failed to install reactive-banana-wx-0.7.1.0
cabal: Error: some packages failed to install:
reactive-banana-wx-0.7.1.0 failed during the configure step. The exception
was:
ExitFailure 1
I'm not sure if this is a reactive-banana-wx problem or some incarnation of cabal hell due to an odd collection of packages on my system. Any suggestions would be appreciated.
This is very annoying, because my fileOpenDialog opens on any button press. Is this correct behavior?
Here is a modified example from Arithmetic.hs to illustrate this issue:
import Data.Maybe
import Graphics.UI.WX hiding (Event)
import Reactive.Banana
import Reactive.Banana.WX
main = do
gui
gui = start $ do
f <- frame [text := "Arithmetic"]
calculate <- button f [text := "="]
input1 <- entry f [processEnter := True]
input2 <- entry f [processEnter := True]
output <- staticText f [size := sz 40 20]
of1Btn <- button f [text := "Open"]
of2Btn <- button f [text := "Open"]
of1Inp <- entry f []
of2Inp <- entry f []
set f [layout := margin 10 $ column 20
[row 10 [label "Open first torrent", widget of1Inp, widget of1Btn],
row 10 [label "Open second torrent", widget of2Inp, widget of2Btn],
row 10 [widget input1, label "+", widget input2, widget calculate, widget output]]]
network <- compile $ do
eenter1 <- event0 input1 command
eenter2 <- event0 input2 command
ebutton <- event0 calculate command
eof1Btn <- event0 of1Btn command
eof2Btn <- event0 of2Btn command
binput1 <- behavior input1 text
binput2 <- behavior input2 text
bofd <- fromPoll $ fileOpenDialog f True True "Open torrent file"
[("Any file",["*.*"]),("Torrents",["*.torrent"])] "" ""
let
ecalculate :: Event ()
ecalculate = ebutton `union` eenter1 `union` eenter2
result :: Discrete (Maybe Int)
result = stepperD Nothing $
(f <$> binput1 <*> binput2) <@ ecalculate
where
f x y = liftA2 (+) (readNumber x) (readNumber y)
readNumber s = listToMaybe [x | (x,"") <- reads s]
showNumber = maybe "--" show
rOpenFile z = stepperD Nothing (bofd <@ z)
showDir = maybe "--" show
sink output [text :== showNumber <$> result]
sink of1Inp [text :== showDir <$> (rOpenFile eof1Btn)]
sink of2Inp [text :== showDir <$> (rOpenFile eof2Btn)]
actuate network
As I understand fromPoll function should only activate on "sink of1Inp" and "sink of2Inp", but it also activates on "sink output".
While the mtl certainly has flaws, and monads-tf may indeed be far superior — I've not used it, so I couldn't say — I find reactive-banana's dependency on it quite troublesome.
For instance, whenever I try to import an mtl module in GHCi, whether from a file or in the REPL, I have to do :set -hide-package monads-tf
to get it to go through because of the conflict.
I'm interested in using reactive-banana for a rather large project, but frankly this one silly issue makes me want to uninstall it!
Unless there's a way to tell GHC to hide it by default that won't cause any problems when using reactive-banana, and I just don't know it (I'd love to hear!), then can I ask how much effort it would take to port reactive-banana to the mtl, and what downsides there might be? Maybe I'm just lucky, but reactive-banana is the only Hackage package I have installed that uses monads-tf, so it seems like the popularity of mtl would outweigh its disadvantages.
Bump the dependencies to the newest wxHaskell 0.13.2 once the latter has stabilized a bit.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.