Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using newEventsNamed #49

Closed
duplode opened this issue Sep 7, 2013 · 4 comments
Closed

Using newEventsNamed #49

duplode opened this issue Sep 7, 2013 · 4 comments

Comments

@duplode
Copy link
Contributor

duplode commented Sep 7, 2013

On noticing newEventsNamed, my first thought was "here is a drop-in replacement for Control.Event.newEventsTagged". The second thought, of course, was "but how do I fire the events"? Eventually, I understood that newEventsNamedshould be the way it is; partly by finding its uses in the rest of Threepenny, and partly from trying to make it equivalent to the old newEventsTagged only to realize it is not as simple as it seems. On the other hand, it is easy to implement newEventsTagged using only the exported functions of Reactive.Threepenny, and it works perfectly well (I use it to create event-based getters without having to expose the corresponding Behavior). Given all of that, I am left with two questions: Is that a sensible use of newEventsNamed? And do you see a place for something in the vein of newEventsTagged in the API?

@HeinrichApfelmus
Copy link
Owner

Your definition looks fine to me, though the use of newEventsTagged is actually optional. How about the following definition:

newEventsTagged :: Eq tag => IO (tag -> Event a, (tag, a) -> IO ())
newEventsTagged = do
    (e,fire) <- newEvent
    return (\tag -> snd <$> filterE ((== tag) . fst) e, fire)

The only advantage of newEventsTagged is that it memoizes the event created for each tag and hence avoids some recomputation. However, you can achieve the same effect by memoizing the returned function by hand.

At the moment, newEventsTagged is more of an internal function that I happened to need, I'm not quite sure what will become of it.

Could you elaborate on your use case? What do you mean by "event-based getters"?

@duplode
Copy link
Contributor Author

duplode commented Sep 7, 2013

Could you elaborate on your use case? What do you mean by "event-based getters"?

The idea is that you fire a request event and listen to a reply from a widget with the relevant value. The tags are needed to have multiple request/reply pairs which do not interfere with each other (as if they were calls to a getter done from different methods in an OOP context). For an example, I implemented them for my embryonic bounded input widget (look for the requestValue/getValue pairs). Implementing getters in this way was more of a necessity with reactive-banana (as then the Behavior backing the getter would be locked in the Moment monad); nonetheless, there still might be some value in this approach if you don't want to expose a particular Behavior for whatever reason (e.g. if you consider it an implementation detail that shouldn't be in the public interface).

@HeinrichApfelmus
Copy link
Owner

Ah, I see. In this case, there is no need for newEventsTagged, you can create a big Event (Tag, a) and filter on the tag a posteriori. I should probably indicate in the documentation that newEventsTagged should be used sparingly.

Also, I'm not entirely sure whether your implementation of event-based getters is a good idea in the first place. There are essentially two ways to work with Behaviors: use the Applicative combinators to combine them, or sample them with an Event. If I understand that correctly, your intention with event-based getters is to restrict yourself to the latter method, in which case it is convient to simply use a partial application of the sampling process.

In code: The standard way to deal with values would be

getValue :: Behavior a

For some reason, you don't want that, so the next best thing would be to offer a function

getValue' :: Event () -> Event a
getValue' = (getValue <@)

which tags a given event ("request a value") with the value of the behavior ("respond with the value"). There is no need for tags at all. Moreover, this formulation has the advantage that the request event and the response event are simultaneous. (Otherwise, there is a delay if you route them through an event firing.)

That said, I don't quite understand why would wouldn't want a Behavior in the first place either. I can sort of understand it, though, because in my experience, it is a good idea to make UI elements only return user events. See a blog post of mine on this matter.

@duplode
Copy link
Contributor Author

duplode commented Sep 8, 2013

Heh, getValue' looks so obvious now that you pointed me to it; I feel quite silly 😄 In the case of my bounded input widget it might be a little less straightforward due to the validation(*), but the approach should be perfectly feasible. Also, thank you for the link to the post; I added an extra comment to it (as bringing that discussion here would be a little off-topic).

(*) In the way it is done now, a request triggers validation; so if the user has, for instance, typed a value above the allowed maximum and there is a request then the returned value should be the maximum and the widget should be updated accordingly. Validating on blur might seem a more sensible thing to do, but it does not cover all relevant situations (e.g. suppose the request was triggered by a keyboard shortcut).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants