Giter Site home page Giter Site logo

heinrichapfelmus / reactive-banana Goto Github PK

View Code? Open in Web Editor NEW
520.0 33.0 71.0 1.99 MB

Library for functional reactive programming in Haskell.

Home Page: https://wiki.haskell.org/Reactive-banana

Makefile 0.70% Haskell 86.19% TeX 13.02% Shell 0.09%
frp haskell reactive-banana-library

reactive-banana's Introduction

Hackage

Reactive-banana

What's this?

Reactive-banana is a library for Functional Reactive Programming (FRP), written in Haskell.

See the project homepage for documentation, examples and so on.

Installation

Compilation from the repository

To build and install the core library from the source repository, simply type

cd reactive-banana && cabal install && cd ..

However, to try out the GUI examples, you have to install one of the of the additional packages.

GUI examples using wxHaskell

Prerequisites: the wxHaskell package

cabal install wx

Note that you need to have a development version of the wxWidgets libraries installed before building wx. If you run into ExitFailure 1 exceptions, please follow the wxHaskell Quick Start instructions and try again.

To build the wx examples, type

cd reactive-banana-wx
cabal configure -fbuildExamples && cabal build
cd ..

Technical overview

How is the source code structured?

The project contains several directories:

  • reactive-banana — the core library
  • reactive-banana-wx — bindings to the wxHaskell GUI library, includes many examples

The reactive-banana library actually contains two FRP implementations:

  1. Reactive.Banana.Model - A model implementation for testing and understanding the semantics. You are encouraged to look at the source code.
  2. Reactive.Banana.Prim - The efficient push-driven implementation used for production code. Contains hard to understand trade secrets. ;-)

License

The source code is distributed under a BSD3 license. See the LICENSE files in the corresponding subdirectories.

The reactive-banana mascot [png] is licensed under a Creative Commons Attribution 4.0 International License with attribution to Heinrich Apfelmus and the reactive-banana library.

Contributors

Many thanks to everyone who contributed, provided feedback or simply wrote an application using Reactive-Banana! In particular, many thanks to:

Alexander Berntsen, Oliver Charles, Samuel Gélineau, Vladimir Lopatin, Atze van der Ploeg, Mitchell Rosen, and many others.

Special thanks to Oliver Charles and Mitchell Rosen for co-maintaining this project!

reactive-banana's People

Contributors

bodigrim avatar danwerner avatar devjac avatar duplode avatar enolan avatar federicobond avatar ggreif avatar gloaming avatar heinrichapfelmus avatar invisible-rabbit-hunter avatar johnlato avatar kraai avatar madjestic avatar mitchellwrosen avatar ocharles avatar peterhil avatar sgraf812 avatar shimuuar avatar skymarshal avatar spinda avatar thielema avatar tomcumming avatar twinside avatar vagarenko avatar zopa avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

reactive-banana's Issues

Unifying Behavior and Discrete

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

  1. Optimisation, and
  2. Interfacing with the outside world.

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:

  • Polling cheap state: There is probably little downside to using a Discrete here, as the GUI framework will already be updating its mutable state based on the flow of events that change it.
  • Polling expensive state based on an event: This one is interesting! I am sceptical that all uses of this pattern are valid, like the RNG I mentioned, but certainly the network statistics example seems reasonable. Obviously, the polling of this state could be pushed inside the IO action an Event builds up, but this seems inelegant. Here's an idea: 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.
  • Code that could easily use Discrete: Well, there's not much to say about this one :)

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:

  • Remove the existing Behavior type.
  • In the Implementation flavour, implement Behavior using Discrete. Replace fromPoll with the form I suggested above.
  • Change the types of initial and changes as follows:
initial :: Behavior a -> NetworkDescription a
changes :: Behavior a -> NetworkDescription (Event a)
  • Document these (or at least changes) as being intended solely for the use of interfacing to the outside world.
  • Reap the benefits of a simplified API, simplified implementation, and vastly simplified programming experience.

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!) :)

Behaviors are sometimes recomputed unnecessarily

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.

no banana.png

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

Three API additions

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.

Proposal: Infinitesimal delays

Splitting this off from issue #25 to stop derailing it :)

The proposal

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?).

Advantages

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

Problems

  • Conal thinks it complicates the semantics (@HeinrichApfelmus: Can you expand on what he found objectionable? Adding infinitesimals to Time seems simple to me.)
  • We don't have a model for it.
  • ?

Install problem

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' withDistribution.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 thepostBuild' 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.

reactive-banana-wx-0.7.0.0 missing Tidings

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.

<<loop>> exception

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)

Implementation differs from model

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:

  • change the imports to switch from Reactive.Banana.Model to Reactive.Banana
  • uncomment the functions at the top which replace names of functions from the model with the functions in the actual implementation
  • change the body of main to switch between the differing interpret functions.

fromPoll IO action activates on any event tied to any sink function.

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".

Assumption of a callback-based, "event driven programming" library is too strong.

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

Optimize Discrete data type by calming simultaneous events

(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.

accumE's type is confusing and not generic enough and too generic

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.

Allow event handlers to be called from inside reactimate

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.)

reactive banana light

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.

Divergence between PushIO and Model implementations

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.

Name `filter` clashes with the one from the Prelude / Data.List

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)?

Type of union is insufficiently general

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.

Memory Leak associated with apply

I've run into a memory leak exemplified by the following code, which fills up about 100MB/sec. The key factors seem to be:

  1. An event foo that is firing rapidly
  2. An event bar 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.
  3. Both events reactimated (in this case to do nothing)

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

Library name and module namespace.

Maybe use just banana, to differentiate it from the already existing reactive library?

Also, namespace pollution. How about Reactive.Banana.Cor etc.

Lack of a filter that simultaneously reveals information about the value of a Behavior

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. :-)

Building fails with ghc-6.12.3 on Ubuntu 11.04

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.

IO bugs in the Asteroids demo program

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

Crash in win x64

It seems, that event0 produces occasional crashes in Windows, below is description and an example.

There are three possible behaviours I observed:

  1. program crashes instantly, and prints error message
  2. program starts, but upon close prints same error
  3. program starts and closes fine

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

reactive-banana-wx BuildFlags problem when installing (GHC 7, Ubuntu)

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

(atomic) event switching on behavour values

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.

Polymorphic time parameter t problematic with overloading of (<@>)

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'

Exceptions and reactimate

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.

Proposal: Disallow multiple simultaneous events

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]]

Proposal

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.

Properties

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. The problem about optimizing the Discrete type (issue #19) disappears.

  6. 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.

Discuss!

What do you think? How would this change affect your existing code base?

RFE: Counter.hs example for reactive-banana (not wx)

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

fromPoll behaviors are not updated before events

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!

`Reactive.Banana.Prim.Order' does not export `empty'

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.

monads-tf dependency?

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.

behaviorText is gone...

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. :)

Proposal: One semantics to bind them all

Several proposals for changes in semantics are floating around right now

  • issue #22 on Behavior replacing Discrete
  • issue #25 on multiple simultaneous events

The cost of switching semantics in an existing library are traditionally high, but I want to facilitate experimentation with the following proposal.

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?

`Spill` fails on the empty list

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.

Typeable constraint on fromAddHandler

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.

Add type paramater to Event, Behavior, NetworkDescription

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.