{-# OPTIONS -fglasgow-exts #-}

----------------------------------------------------------------------
-- |
-- Module      :  Control.DataDriven
-- Copyright   :  (c) Conal Elliott 2007
-- License     :  LGPL
-- 
-- Maintainer  :  conal@conal.net
-- Stability   :  experimental
-- Portability :  portable
-- 
-- Data-driven computations
----------------------------------------------------------------------

module Control.DataDriven
  (
  -- * Plumbing for \"events\" and subscription 
    Sink, Action, News
  -- * Data-driven computations
  , DataDrivenG, dd, mapCur
  , DataDriven, runDD, joinDD
  ) where

import Control.Applicative
import Control.Monad (join)
import Control.Arrow (second)

import Data.Monoid

import Control.Compose


{----------------------------------------------------------
    Plumbing for event publishing
----------------------------------------------------------}

-- | Sinks (consumers) of values
type Sink cur a = a -> Action cur

-- | Actions
type Action cur = cur ()

-- | News publisher -- somewhere to register updaters to be executed
-- when events occur.
type News cur = Sink cur (Action cur)


{----------------------------------------------------------
    Data-driven computations
----------------------------------------------------------}

-- | The general type of data-driven computations.  Represented as a
-- /news/ publisher (@news@) and a way to get new values (@cur@).  Clients
-- interested in the value subscribe to @news@ and extract a new value
-- from @cur@ when notified that the value may have changed.  When @news@
-- is a monoid and @cur@ is an applicative functor, @DataDrivenG news cur@
-- is an applicative functor also.  The applicative property is very
-- convenient for composition.  See the more specific type 'DataDriven'.
--
-- Nicer, but Haddock chokes on the infix op:
--   type DataDrivenG news cur = ((,) news) `O` cur

type DataDrivenG news cur = O ((,) news) cur

-- More tersely :
-- type DataDrivenG news = O ((,) news)

-- | Construct a data-driven computation from a subscription service
-- (@Monoid@) and a value source subscriber (@Applicative@).
dd :: cur a -> news -> DataDrivenG news cur a
dd = flip (curry O)

-- | Modify the source part of a 'DataDriven' computation.
mapCur :: (cur a -> cur b) -> (DataDrivenG news cur a -> DataDrivenG news cur b)
mapCur f = inO (second f)


-- | Data driven with news publisher
type DataDriven cur = DataDrivenG (News cur) cur


-- | Run a unit-valued 'DataDriven' computation.  Causes the source to be
-- executed /and/ registered with the subscriber.
runDD :: (Monoid (Action cur), Applicative cur)
      => DataDriven cur () -> Action cur
runDD (O (news,cur)) = news cur `mappend` cur

-- | Apply 'join' to a source
joinDD :: Monad cur => DataDriven cur (cur a) -> DataDriven cur a
joinDD = mapCur join

-- runDDJoin :: (Monad cur, Applicative cur, Monoid (Action cur))
--           => DataDriven cur (Action cur) -> Action cur
-- runDDJoin = runDD . joinDD