I’ve been looking for a way to do the pieces of I/O that are well-defined in the framework of FRP. For example, fileContents "someFile" is a perfectly good function of time, why should we be forced to drop into the semantic fuzziness of the IO monad to get it?
Well, after a long talk with the Anygma folks about it, getting not very far in the practical world, I decided to do something else to quench my thirst for the time being since software needs to get written. I’m going to have the FRP program request actions by sending sinks to the top level, and then have the results of those requests come back via futures. So:
type Sink a = a -> IO () type Pipe = forall a. (Future a, Sink a) data Stream a = a :> Stream a liftFRP :: Sink a -> IO a -> IO () liftFRP sink a = a >>= sink
Okay, that’s nice, but how do we prevent uses of these things from becoming a tangled mess. Input always needs to communicate with output, and it seems like it would just be a pain to coordinate that. It turns out we can stick it in a monad:
newtype StreamReaderT v m a = SRT { runSRT :: StateT (Stream v) m a) }
deriving (Functor, Monad, MonadTrans)
readNext = SRT $ do
(x :> xs) <- get
put xs
return x
type IO' = StreamReaderT Pipe (WriterT (IO ()) Future)
-- using the IO () monoid with mappend = (>>)
liftFRP :: IO a -> IO' a
liftFRP io = do
(fut,sink) <- readNext
tell (io >>= sink)
(lift.lift) fut
unliftFRP :: IO' a -> IO a
unliftFRP m = do
stream <- makePipeStream -- a bit of magic here
((fut,_),action) <- runWriterT (runStateT (runSRT m) stream)
action
return $! futVal fut
It looks like IO’ has the very same (lack of) semantics as IO. liftFRP and unliftFRP form an isomorphism, so we really can treat them as pulling apart the IO monad into something more versatile, and then putting it back together.
Also we get nice fun parallel version of liftFRP. I can’t decide if this should be the common one or not.
liftParFRP :: IO a -> IO' a
liftParFRP io = do
(fut,sink) <- readNext
tell (forkIO (io >>= sink))
(lift.lift) fut
So using the liftFRP and unliftFRP, we are no longer “trapped in IO” as some folks like to say. We can weave in and out of using IO as we’re used to and using requests and futures when convenient. For example, it’s trivial to have a short dialog with the user via the console, and have the real time program ticking away all the while. Fun stuff!

Where’s your source for Futures?
Source code, you mean? That’s in the reactive library which is being hacked on at the moment.
http://code.haskell.org/reactive
The CSS on your site is causing an Athlon64 3500 to grind. This is just wrong.
Yeah, but it’s pretty, ain’t it? We are Haskell programmers: we often sacrifice speed for beauty.