Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.
Sign upMove away from a parametric approach to trimming (preferring a monadic approach) #97
Comments
This comment has been minimized.
This comment has been minimized.
jkozlowski
commented
Jun 3, 2015
|
+1
I have recently tried to master this (while following https://github.com/ocharles/Francium todo-mvc) and I would also vote for a simpler approach. |
This comment has been minimized.
This comment has been minimized.
miguel-negrao
commented
Jun 5, 2015
|
For those not using dynamic event switching, accumB becoming monadic might actually be more annoying then dealing with the type issues, since these don't appear that much, while making accumB monadic means abstracting parts of the event graph into functions requires writing functions working on monadic values, which is makes things more complicated.
I feel it would be a real pity to remove this feature... although I can understand why it might be needed. |
This comment has been minimized.
This comment has been minimized.
|
I get that, but in reality I'm yet to write many applications that don't
|
This comment has been minimized.
This comment has been minimized.
|
One more argument in favour of this - even with newtype Francium t a =
Francium {runFrancium :: Frameworks t => ReaderT (FranciumEnv t) (WriterT (Behavior t [MarkedNode]) (Moment t)) a}
deriving (Functor,Applicative,Monad,MonadFix,MonadReader (FranciumEnv t),MonadWriter (Behavior [MarkedNode]))So the author has to manually create all the instances. That's a fair bit of faff for someone who just wants to abstract over |
HeinrichApfelmus
referenced this issue
Aug 26, 2015
Closed
'Identity' in reactive-banana collides with 'Identity' in base #80
HeinrichApfelmus
added this to the 1.0 milestone
Aug 30, 2015
This comment has been minimized.
This comment has been minimized.
|
The latest commit 4313823 in the |
This comment has been minimized.
This comment has been minimized.
|
This is looking really good! One quick question - is there anyway to |
This comment has been minimized.
This comment has been minimized.
Observe: The duplication is unfortunate, but I think it's important to distinguish a pure and an impure variant. |
This comment has been minimized.
This comment has been minimized.
|
let clockOnKeyPress = observeE (fmap (const (integrate stepPhysics 0 1)) keyPressed)
in ...
-- later
integrate dt initial x =
trace "integrate" $ accumB initial ((.+^) <$> ((^*) <$> x <@> (dt :: Event Double)))Yet despite pressing the key multiple times, I always get the same integration (when I would expect the |
This comment has been minimized.
This comment has been minimized.
|
Yes, please open another issue, this could be a bug with |
This comment has been minimized.
This comment has been minimized.
|
Actually, no need to open another issue, it's definitely a bug. |
This comment has been minimized.
This comment has been minimized.
|
The new API has been merged into master and the test suite succeeds as of commit ff0b914 . Everything is ready to be tried out, this is the code that will likely make up the 1.0 release. |
This comment has been minimized.
This comment has been minimized.
|
Thanks a lot for your input everyone! I think this issue can be closed now that the monadic API has been implemented. |
ocharles commentedMay 27, 2015
This is still for discussion I believe, but here's the discussion I had with @HeinrichApfelmus:
Hi Heinrich,
Finally having another chance to play around more with reactive-banana for putting together web UIs and continuing to really enjoy it. The only pain point is mostly around dynamic event switching and the necessity of trimming. As a recent example, I have a list of strings that changes over time, and I need a set of toggle buttons for each of these strings in the set. Currently, this leads to code such as:
Which is rather verbose for what it's doing! Here classTypesChanged :: Event t [String], and I fold this set into both an Event t ([String] -> [String]) (to update the users selection) and also a Behavior t HTML value, which lays out all components into a single piece of HTML (with event handlers wired in appropriately). Essentially I have to do a fair amount of coding to switch between "future" occurances (AnyMoment) and the current point in time - and that seems to require a lot of juggling (I'm yet to be able to fully abstract this away).
I don't have a concrete question about this code - it works fine, but am mostly using it as a talking point. My general question is - what are your plans for the
trepresentation in order to know when to trim? As far as I understand it, you need either this or a monad - the monadic approach being taken by sodium and threepenny-gui. Do you think reactive-banana will continue to use parametricity, or will eventually move to a monad too?Curious to hear your thoughts,
Ollie
Dear Oliver,
what is your preference?
Currently, I am indeed leaning towards the monad, as used in threepenny-gui or sodium. The thing that irks me the most is that every widget, like your InstantiatedComponent, needs to be tagged with its time of creation, t, forcing you to keep track of it through each and every type signature. Ugh! I have also received questions from people who had a hard time juggling the AnyMoment types and were very confused by various type errors – you seem to have figured it out, at least. :-) The situation would slightly better if GHC would support impredicative types properly, but this is yet another can of worms.
However, I am quite unsure on how to proceed in making the switch. I have already extracted the core functionality into the Reactive.Banana.Prim module, so that it is possible to implement either model – monad or start time parameter – on top of it. I could offer both variants, and make one the default, or switch everything over to the monadic variant, which would totally break backwards compatibility, though. Do you have an opinion on this?
Best regards,
Heinrich
Dear Oliver,
Hard to say - I haven't used the monadic approach! But my feeling is that I do tend to use dynamic switching more than I might have expected, as most of my FRP work is around UIs (be that GTK or in the browser). Every time I have to switch dynamically, my mood drops a little because I know I have to do more plumbing than appears necessary. Due to the use of rank-n types, it also means that a lot of stuff is very hard to abstract (I have needed to introduce existential types), and common techniques don't even apply any more (point-free programming is hard in rank-n types, often (\x -> foo (bar x)) cannot be rewritten as (foo . bar).
I've definitely figured it out, but that's not to say it's become intuitive - this area is usually the only pain-point I encounter with reactive-banana. My general impression is that while the whole notion of parametricity is really nice in theory, in practice it just doesn't play out as nicely. To me, reactive-banana is a beautiful API, and it's a shame that we lose some of this with AnyMoment and friends.
Continuing the above suggestion that reactive-banana is very much do-what-I-mean, I would advise against offering more than you have to. Thus I'd say if the monadic interface is equivalent in power/expressivity as the parametricity approach (or better!) then that's the one to go with.
With respect to the Prim namespace, are you actually using this anywhere? I see that threepenny-gui doesn't actually use this, but instead has its own FRP network.
Hope this helps!
Dear Oliver,
thanks a lot for your thoughts!
Yeah, I completely agree.
Unfortunately, while the monadic API simplifies switching a lot, it has one big pain point: the type of accumB will become monadic:
Same for accumE. This means that whenever you accumulate state, this becomes visible in the types. This was also the original reason why I came up with current approach: With the phantom type, all functions are pure as long as you don't use switching. I thought this would be useful for beginners, but it seems that the learning curve for switching becomes too high, then.
I'm afraid it's a "pick your poison" situation: To avoid time leaks, one has to pay attention to starting types in the types, be it by phantom type or with a monad.
The intention is for Threepenny to switch to reactive-banana at some point and use the Prim namespace to build its FRP combinators. Apparently, this will still take some time. :-)
Speaking of Threepenny: There is another important difference between its FRP engine and the reactive-banana one when it comes to simultaneous events. In reactive-banana, an Event may have multiple occurrences at the same time. This is due to the
combinator, which has to handle the situation where both arguments have an occurrence at the same time. In contrast, Threepenny disallows multiple simultaneous occurrences and only provides a
combinator that applies the function when both event arguments have a simultaneous occurrence.
These days, I prefer the second approach, because in the following snippet
the events eOut and eOut' are equal with the latter approach, but not with the former. Taking the union of two events has become more difficult, but I think that it is a good idea to force the programmer to think whenever he wants to take the union of events, because it can be the source of subtle bugs.
What is your opinion on this?
Best,
Heinrich
While this is true, I don't think it's really quite that bad. Almost all use of reactive-banana ends up living inside a monadic do block anyway - with let bindings introduced locally. I don't think I've ever really introduced combinators or logic outside a do block anyway. While this is a poison, it seems to require a lot more of it for it to be harmful :) I think that this actually comes out easier in the long run for beginners - while they might have to switch between pure/monadic binding, they can otherwise follow the types. Switching in particular is something where following the types has a lot less hopes. Right now you're required to provide an AnyMoment, which in turn means you have to trim, and then you need a FrameworksMoment, and then an execute, and then you've forgotten what you were donig! :)
I don't have much of an opinion on this because I don't think I've ever really had simulatenous firings of events. While you say in other writing that it's easier to introduce them with other combinators (that is, further processing after the GUI event fires), I haven't given it much thought. Certainly it's been quite nice to be able to write things like
unions [addItem, removeItem], but I suppose in the new formulation I'm just using unionWith (.) - which is just as clear. It sounds like this would get rid of the experimental Calm API too - an API I've never touched and always wondered why I'd want to use it :) (I see you use it in tidings, but I never fully understood why).thanks for your input!
It is pretty much decided, then, that I will move reactive-banana to a monadic API and also remove simultaneous events in favor of unionWith (That's what I did in Threepenny and no one even noticed so far). No promises on how soon, though. :-)
Best,
Heinrich