Closed duplode closed 11 years ago
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"?
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).
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.
Heh, getValue'
looks so obvious now that you pointed me to it; I feel quite silly :smile: 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).
On noticing
newEventsNamed
, my first thought was "here is a drop-in replacement forControl.Event.newEventsTagged
". The second thought, of course, was "but how do I fire the events"? Eventually, I understood thatnewEventsNamed
should 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 oldnewEventsTagged
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 ofReactive.Threepenny
, and it works perfectly well (I use it to create event-based getters without having to expose the correspondingBehavior
). Given all of that, I am left with two questions: Is that a sensible use ofnewEventsNamed
? And do you see a place for something in the vein ofnewEventsTagged
in the API?