HeinrichApfelmus / threepenny-gui

GUI framework that uses the web browser as a display.
https://heinrichapfelmus.github.io/threepenny-gui/
Other
439 stars 77 forks source link

Threepenny.Reactive and Reactive.Banana.Threepenny #41

Closed duplode closed 11 years ago

duplode commented 11 years ago

I have noticed that work on integrated FRP has already stared in the embed-frp branch. I realize that I am probably jumping the gun by raising this issue now, given how very new that is, but anyway: do you plan to support reactive-banana-threepenny once 0.3.0.0 is released?

Context: I have just attempted to build my current project with the head of embed-frp, hoping that it would take little more than averting name clashes and replacing the Control.Event functions with the corresponding Threepenny.Reactive ones. Obviously that didn't work, as things like blur in Graphics.UI.Threepenny.Events now have quite different types.Of course, there is no urgency about it (just me being nosy and trying to build stuff off the development head!), but I would like to plan ahead in case there will be a need to, e.g. port my code (and any missing reactive-banana combinators it might need) to Threepenny.Reactive, or writing an alternative Control.Event able to bridge reactive-banana and threepenny 0.3.*.

HeinrichApfelmus commented 11 years ago

Do you plan to support reactive-banana-threepenny once 0.3.0.0 is released?

No and yes. My goal is to make FRP really easy to use for GUI programming. I'm still experimenting, but everything seems to point in the direction that the GUI library should depend on the FRP package. Hence, reactive-banana-threepenny will be obsolete very soon and Threepenny will export all the FRP stuff needed.^*

However, they are still compatible in the following sense: By using only the newEvent and register functions, Threepenny can be used as if it had a traditional imperative event-based API, and you can use this in conjunction with the reactive-banana as before. I'm not sure why you would want to do that, but at least you can. In this sense, the reactive-banana-threepenny module should continue to work, even though you need to update a few function names and types. (addHandler has become register).

^* Threepenny is currently not using reactive-banana for the FRP implementation, but they will be merged back/forward eventually.


In short: you can keep reactive-banana-threepenny if you really want to (though you would have to update it), but I would recommend to switch to Reactive.Threepenny. Coming from the same author, you can be sure that it will (eventually) offer the same functionality as reactive-banana. :smile:

Sorry that the whole thing is experimental and moving very fast, but the "best" API is still unkown and the only way to find out is to experiment ruthlessly. :boom: :telescope:

duplode commented 11 years ago

I'm not sure why you would want to do that, but at least you can.

No good reason (I am certainly not trying to abstract my widgets from the GUI library!), mostly laziness, i.e. trying to find an excuse not to port the code. :smile: In any case, your answer assures me that Reactive.Threepenny is the way forward. I will likely open an experimental branch in my repository and start converting the code ASAP, while there are still just two event networks to port. A quick glance at ed74258d72d452ed1bf165abf116d1962f965bc1 shows I won't miss any functionality (the only thing I don't see there is union, which, if I'm correct, can be trivially defined as unionWith const).


P.S.: As for the fast-moving API, no need to say sorry of course - that was part of the deal when I settled for a cool, brand new GUI library within a week of its initial release. :wink:

HeinrichApfelmus commented 11 years ago

Concerning union, the Threepenny variant unionWith const does differ from the reactive-banana variant in that reactive-banana allows multiple simultaneous event occurrences while Threepenny allows only one event occurrence at each point in time, so the function . Both have their advantages and drawbacks, but I'm currently favoring the latter one because the most common cases are Event () and Event (a -> a).

It may be useful to use Event (a -> a) as a monoid in the following way

concatE :: [Event (a -> a)] -> Event (a -> a)
concatE = foldr never (unionWith (.))
duplode commented 11 years ago

From my limited experience, the common use cases of Event a with a not being a function are stepper, in which is clearly better not to have simultaneous occurrences, and reactimate (or register in Reactive.Threepenny), in which unionWith const means only the action for the first event happens. In the latter case, would having something like

sequenceE :: [Event a] -> Event [a]

and then calling sequence_ in the handler be a viable workaround?

HeinrichApfelmus commented 11 years ago

Yep, sequenceE is a good solution. In fact, that's how reactive-banana implements it internally.

I wonder if there is a better name, though, as Event is not a monad. Maybe call it

unions :: [Event a] -> Event [a]
unions = foldr never (unionWith (++)) . map (fmap (:[]))
duplode commented 11 years ago

Yup, unions sounds good, as the homonymous function from Reactive.Banana.Combinators won't have a direct equivalent for the reasons you just explained.

duplode commented 11 years ago

Addendum: having started to convert the code, I now see there was little reason to worry in first place. It's been sweet... Merely not needing fromAddHandler . register or to double the <$> is enough to sell me on it!

HeinrichApfelmus commented 11 years ago

The new FRP works great for me as well, I have merged it into the develop branch and deleted the embed-frp branch.