[reactive] Event's Monad instance and joinE

Chris Lesniewski-Laas ctl at MIT.EDU
Fri Jul 10 10:39:16 EDT 2009


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


More information about the Reactive mailing list