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

Lack of a filter that simultaneously reveals information about the value of a Behavior #17

Closed
gcross opened this issue Oct 26, 2011 · 2 comments

Comments

@gcross
Copy link

gcross commented Oct 26, 2011

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

@HeinrichApfelmus
Copy link
Owner

Thanks for your feedback!

Fortunately, the semantics of reactive-banana are awesome and do everything you want already. :-) In particular,

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.

it is guaranteed that the value of the behavior b "doesn't change in between". I'm using parentheses here because the notion of something "changing in between" is a actually concept that simply cannot occur in reactive-banana (since version 0.2, that is).

The semantics of reactive-banana are given by the [model implementation in the module Reactive.Banana.Model]. I have kept it very simple, it should be easy to understand. These semantics are law. Concurrency or not, if the efficient implementation does not satisfy them, I'm going to jail! :-O

In particular, this means that your implementation is indeed correct. Here a slightly tidied up version:

filterJust :: Event (Maybe a) -> Event a
filterJust = fmap fromJust . filterE isJust

e2a = filterJust    $ b <@ e1
e2b = filterNothing $ b <@ e1
    where filterNothing e = () <$ filterE isNothing e

That said, I agree that the filterJust function is a useful shortcut and should probably be added to the API.

@HeinrichApfelmus
Copy link
Owner

I have now added the filterJust function to the API. See issue #18.

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