module FRP.Fursuit.Async where
import Control.Applicative
import FRP.Fursuit.Signal
import FRP.Fursuit.Sink
import FRP.Fursuit.Pipe

-- | Create an asynchronous signal. When the signal is triggered, it should
--   arrange for a value to written into its obtained pipe at some point in
--   the future; when that happens, the signal continues on its path.
--
--   The signal setup action is guaranteed to be called exactly once per
--   received signal; thus the following code will print 1 two times rather
--   than 1 followed by 2:
-- @
--   ref <- newIORef ()
--   (p, s) <- pipe ()
--   sig <- async $ (\p -> modifyIORef ref (+1) >> write p ref) <$ s
--   sink (\r -> readIORef r >>= print) sig
--   sink (\r -> readIORef r >>= print) sig
--   write p ()
-- @
--   Async is primarily intended as a building block for other more high level
--   signals, and should be used with extreme care as it permits arbitrary side
--   effects to happen in the course of propagating a signal.
--
--   It's also worth noting that triggering the signal returned by async does
--   not trigger the setup signal, but will instead use the last value held by
--   the output signal, if any.
async :: Signal (Pipe a -> IO ()) -> IO (Signal a)
async setup = do
  (p,s) <- emptyPipe
  perform $ setup <*> pure p
  return s