Event's Monad instance and joinE

Hi. I've been experimenting with using Reactive as the basis of a network daemon I'm working on, with the top-level type Event Packet -> Event Packet. I've run into a difficult-to-debug strictness problem with the Monad instance for EventG, at least when used together with makeEvent. Here's a minimal test program that shows the problem:
import FRP.Reactive import FRP.Reactive.LegacyAdapters import Control.Concurrent import Control.Monad
my_event :: IO (Event Char) my_event = do (char_sink, char_event) <- makeEvent =<< makeClock forkIO $ forever $ char_sink =<< getChar return char_event
main = do chars <- my_event adaptE $ do c <- chars ; return (print c)
Running this program consumes 100% of CPU and doesn't respond to input. On the other hand, replacing the last line with
adaptE $ print `fmap` chars
does what you'd expect (prints each character read from stdin as it is received). I'm pretty sure what's happening here is adaptE evaluating joinE, which is in turn evaluating its recursive call to joinE without being properly lazy and waiting for the next occurrence of Event (Event Char), leading to an infinite loop. I've played around with the definition of joinE, but I don't have a sufficiently good mental model of the evaluator to figure out what needs to become stricter or lazier to fix this problem. In any case, my changes have either resulted in stack overflows or evaluating mempty. Perhaps I am misunderstanding the contract for Event's Monad instance. Is there a reason why it shouldn't be used with makeEvent or even adaptE? mkUpdater would be a poor substitute for adaptE, since a network daemon doesn't have any obvious equivalent to a graphical program's idle loop; ideally the output should be driven by incoming packets. Thanks, Chris

On Fri, Jul 10, 2009 at 4:39 PM, Chris Lesniewski-Laas
Hi. I've been experimenting with using Reactive as the basis of a network daemon I'm working on, with the top-level type Event Packet -> Event Packet.
I've run into a difficult-to-debug strictness problem with the Monad instance for EventG, at least when used together with makeEvent. Here's a minimal test program that shows the problem:
I can't comment on this exact bug, but in general the Monad instance for Event is semantically broken. There's a perfectly reasonable joinE function, yes, but as I understand it combining it with the perfectly reasonable Applicative instance produces an illegal monad. Thus, in general, you should avoid the Monad instance and use joinE directly when required. -- Svein Ove Aas

On Fri, Jul 10, 2009 at 04:47:49PM +0200, Svein Ove Aas wrote:
I can't comment on this exact bug, but in general the Monad instance for Event is semantically broken.
There's a perfectly reasonable joinE function, yes, but as I understand it combining it with the perfectly reasonable Applicative instance produces an illegal monad. Thus, in general, you should avoid the Monad instance and use joinE directly when required.
For what it's worth, the same problem appears to be present if I use joinE directly instead of join/>>=. I've experimented a lot with variations on joinE's implementation. If it is going to be possible for one input packet to result in multiple output packets, I'm going to need some function from Event (Event a) to Event a. switchE is clearly not it. joinE seems to be the right idea, although I'm unclear on the semantics when an inner event has occurrences predating its own occurrence in the Event (Event a). Chris

Hi Chris,
Your use for joinE is exactly the sort of thing I had in mind for the Event
monad. The semantics is to combine all of the generated events via mappend,
which means the occurrences will be time-sorted. But first, the inner
events are time-tweaked so as not to pre-date the occurencs that gave birth
to them. Have you read "Push-pull functional reactive programming" (or its
nearly-identical predecessor "Simply efficient functional reactivity)? The
semantics is spelled out precisely there, in terms of the monad instance for
Future. The latter monad is determined by the semantics of Future
(time/value pairs, with a suitable monoid for time), according to the "type
class morphism" property (subject of another paper). If there's something
in the paper that's not clear to you, feel free to ask for clarification.
I think there's a misunderstanding behind Svein's comment about a conflict
between Applicative & Monad instances for Event. I gave both instances in
the paper, and mentioned why I don't expect the Applicative instance to be
very useful. I hinted at an alternative (relative time, which simply
changes the Time monoid from Max to Sum), which would lead to different
Applicative & Monad instances -- again consistent with each other -- where
I'd expect the Applicative instance to be more useful (because sums have
more variety than maxima).
There is a problem with the monad associativity law in the current Event
Monad semantics. I doubt that semantic problem has anything to do with the
implementation problem in joinE. I'd guess there's a subtle laziness issue.
Regards, - Conal
On Fri, Jul 10, 2009 at 7:54 AM, Chris Lesniewski-Laas
On Fri, Jul 10, 2009 at 04:47:49PM +0200, Svein Ove Aas wrote:
I can't comment on this exact bug, but in general the Monad instance for Event is semantically broken.
There's a perfectly reasonable joinE function, yes, but as I understand it combining it with the perfectly reasonable Applicative instance produces an illegal monad. Thus, in general, you should avoid the Monad instance and use joinE directly when required.
For what it's worth, the same problem appears to be present if I use joinE directly instead of join/>>=. I've experimented a lot with variations on joinE's implementation.
If it is going to be possible for one input packet to result in multiple output packets, I'm going to need some function from Event (Event a) to Event a. switchE is clearly not it. joinE seems to be the right idea, although I'm unclear on the semantics when an inner event has occurrences predating its own occurrence in the Event (Event a).
Chris _______________________________________________ Reactive mailing list Reactive@haskell.org http://www.haskell.org/mailman/listinfo/reactive
participants (3)
-
Chris Lesniewski-Laas
-
Conal Elliott
-
Svein Ove Aas