Skip to content

Signals

louispan edited this page Jan 12, 2018 · 2 revisions

Elm Signals

Main ideas

Signal a -- a effectful flow of values
Address a -- an inbox that can be sent values

Elm Signal a corresponds to

Pipes.Concurrent.Input a
Monad m => Pipes.Producer a m ()

Elm Address a corresponds to

Pipes.Concurrent.Output a
Monad m => Pipes.Consumer a m ()

Limitations of using Input a for Signal a:

liftA2 :: (a -> b -> c) -> 
Signal a -> Signal b -> Signal c
  • The Applicative instance of Input a only combines synchronously, ie. a value is returned only after both inputs produce a value.

Impulse

newtype Impulse m a = Impulse {
  impulsively :: P.Producer a m ()  }

pipes-fluid provides Impulse with a Applicative instance that can reactively combines two producers, given initial values to use when the producer is blocked/failed.

  • This only works for Alternative m where failure means there was no effects, eg. Control.Concurrent.STM, or MonadTrans t => t STM
  • NB.No monad instance
apImpulse :: (Alternative m, Monad m) =>
    Maybe (a -> b) -> Maybe a
    -> Impulse m (a -> b) -> Impulse m a -> Impulse m b

instance (Alternative m, Monad m) => Applicative (Impulse m) 
 where
  pure = Impulse . P.yield -- “once”  
  -- 'ap' doesn't know about initial values
  (<*>) = apImpulse Nothing Nothing

ImpulseIO

newtype ImpulseIO m a = ImpulseIO {
  impulsivelyIO :: P.Producer a m ()  }

instance (MonadBaseControl IO m, Forall (A.Pure m)) => Applicative (ImpulseIO m)
  • uses lifted-async package to run both producers in two lightweight threads.

Simultaneous

newtype Simultaneous m a = Simultaneous {
  simultaneous :: P.Producer a m ()  }
  • The applicative instance for Simultaneous will produce a value when both producers produce a value.
  • Functor, Applicative, and Monad instances for Simultaneous for all Monad m.
  • NB. Can't use Input a because it is a newtype wrapper around STM (Maybe a), not Producer a m ()
instance Monad m => Applicative (Simultaneous m) where
  pure = Simultaneous . forever . P.yield -- constant/always 
  Simultaneous fs <*> Sync as = Simultaneous $ do
    rf <- lift $ P.next fs
    ra <- lift $ P.next as
    case (rf, ra) of
      (Left _, _) -> pure ()
      (_, Left _) -> pure ()
      (Right (f, fs'), Right (a, as')) -> do
        P.yield $ f a
        simultaneously $ Simultaneous fs' <*> Simultaneous as'
Clone this wiki locally