HeinrichApfelmus/reactive-banana — Issue #17: Lack of a filter that simultaneously reveals information about the value of a Behavior
Author: gcross
Created: 2011-10-26 09:45:59+00:00
State: open

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