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
_______________________________________________
Reactive mailing list
Reac...@haskell.org
http://www.haskell.org/mailman/listinfo/reactive
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
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