
I've been worrying about what happens when monads end up caught inside an Event stream, as they inevitably do. Usually this is the IO monad, & usually it's not a problem because adaptE deals with Event (IO a) types eventually. But look at the following (contrived) example: I read lots of people's mail. I have a load of buttons which add a person's mail to the set I receive, summarised by: addPerson :: Event Person I want an event of all the mail I get. Given: getMail :: Person -> Event Mail I can do: allMail :: Event Mail allMail = addPerson >>= getMail But realistically, I might have to create a new event for each person, or look the event up in a database or something. Then I have only: getMailM :: Person -> IO (Event Mail) So, fmap getMailM addPerson :: Event (IO (Event Mail)) and there is no longer a function (like "join" above) for getting an Event Mail, an Event (IO Mail), an IO (Event Mail) or even an IO (Event (IO Mail)) out of that as things stand. Given a clock, I could do something like:-- undoIO :: Clock t -> Event (IO a) -> IO (Event a) undoIO c e = do (sink, e') <- makeEvent c forkIO $ adaptE (>>= sink) e return e' But having a clock lying around seems wrong; and I'd ideally like the new Event to be simultaneous with the old one (provided getMailM finishes quickly). I could also do this an old-fashioned way by passing around event sinks; but again that's not really in the spirit. Also, these fixes will only stand a chance with IO (or ST, maybe); what if getMailM :: Person -> State Foo (Event Mail) ? So, can a function be constructed with the desired type (say Event (m (Event a)) -> m (Event a)), or is that unreasonable to expect? Is there a (generalizable) alternative way of doing this example which doesn't require one? This seems a natural thing to want to do. Freddie Manners

On 23 Mar 2009, at 18:38, Freddie Manners wrote:
I've been worrying about what happens when monads end up caught inside an Event stream, as they inevitably do. Usually this is the IO monad, & usually it's not a problem because adaptE deals with Event (IO a) types eventually. But look at the following (contrived) example:
I read lots of people's mail. I have a load of buttons which add a person's mail to the set I receive, summarised by: addPerson :: Event Person I want an event of all the mail I get. Given: getMail :: Person -> Event Mail I can do: allMail :: Event Mail allMail = addPerson >>= getMail But realistically, I might have to create a new event for each person, or look the event up in a database or something. Then I have only: getMailM :: Person -> IO (Event Mail)
Why might that be the type? This sounds like the getMail function, only with a chunk of legacy adapter exposed in your pure code.
Also, these fixes will only stand a chance with IO (or ST, maybe); what if getMailM :: Person -> State Foo (Event Mail) ?
The simple answer is – don't! IO and State are methods of describing time based computation, but we already have a lovely pure functional description of time based computation – Reactive. Your IO should end in the legacy adapter. Bob

Thanks, Bob. I agree entirely. FRP is a replacement for the imperative
(IO) way of thinking. The IO exposed in Reactive is solely for making
legacy adapters. Applications would have no visible IO. Why? Because
(non-toy) imperative programming has intractable denotation and hence is
troublesome to compose and reason about. - Conal
On Mon, Mar 23, 2009 at 3:48 PM, Thomas Davie
On 23 Mar 2009, at 18:38, Freddie Manners wrote:
I've been worrying about what happens when monads end up caught inside an
Event stream, as they inevitably do. Usually this is the IO monad, & usually it's not a problem because adaptE deals with Event (IO a) types eventually. But look at the following (contrived) example:
I read lots of people's mail. I have a load of buttons which add a person's mail to the set I receive, summarised by: addPerson :: Event Person I want an event of all the mail I get. Given: getMail :: Person -> Event Mail I can do: allMail :: Event Mail allMail = addPerson >>= getMail But realistically, I might have to create a new event for each person, or look the event up in a database or something. Then I have only: getMailM :: Person -> IO (Event Mail)
Why might that be the type? This sounds like the getMail function, only with a chunk of legacy adapter exposed in your pure code.
Also, these fixes will only stand a chance with IO (or ST, maybe); what if
getMailM :: Person -> State Foo (Event Mail) ?
The simple answer is – don't! IO and State are methods of describing time based computation, but we already have a lovely pure functional description of time based computation – Reactive. Your IO should end in the legacy adapter.
Bob_______________________________________________ Reactive mailing list Reactive@haskell.org http://www.haskell.org/mailman/listinfo/reactive

Thanks. The (non-contrived) motivation was related to trying to link
external databases into reactive code; my attempts to make legacy adapters
for database calls seemed very unnatural, so Event (IO a) everywhere looked
like an option. I will give the pure approach another hack.
Freddie
2009/3/24 Conal Elliott
Thanks, Bob. I agree entirely. FRP is a replacement for the imperative (IO) way of thinking. The IO exposed in Reactive is solely for making legacy adapters. Applications would have no visible IO. Why? Because (non-toy) imperative programming has intractable denotation and hence is troublesome to compose and reason about. - Conal
On Mon, Mar 23, 2009 at 3:48 PM, Thomas Davie
wrote: On 23 Mar 2009, at 18:38, Freddie Manners wrote:
I've been worrying about what happens when monads end up caught inside an
Event stream, as they inevitably do. Usually this is the IO monad, & usually it's not a problem because adaptE deals with Event (IO a) types eventually. But look at the following (contrived) example:
I read lots of people's mail. I have a load of buttons which add a person's mail to the set I receive, summarised by: addPerson :: Event Person I want an event of all the mail I get. Given: getMail :: Person -> Event Mail I can do: allMail :: Event Mail allMail = addPerson >>= getMail But realistically, I might have to create a new event for each person, or look the event up in a database or something. Then I have only: getMailM :: Person -> IO (Event Mail)
Why might that be the type? This sounds like the getMail function, only with a chunk of legacy adapter exposed in your pure code.
Also, these fixes will only stand a chance with IO (or ST, maybe); what
if getMailM :: Person -> State Foo (Event Mail) ?
The simple answer is – don't! IO and State are methods of describing time based computation, but we already have a lovely pure functional description of time based computation – Reactive. Your IO should end in the legacy adapter.
Bob_______________________________________________ Reactive mailing list Reactive@haskell.org http://www.haskell.org/mailman/listinfo/reactive
participants (3)
-
Conal Elliott
-
Freddie Manners
-
Thomas Davie