-- | Type for "pipes", having multiple writers and readers.
--
-- Each element  (of type 'a') written to the pipe is "presented" to each reader (continuation) (of type @a -> r@).
--
-- Elements and readers can be adder in any order. When a new element is added, it is presented to the existing readers. When a new reader is adder, it is presented with the existing elements.
--
-- This type is used internally in the implementation of 'EarleyM', but it may be more widely useful.

module EarleyM.Pipe(
  Pipe, empty,
  write, read,
  firstWrite, firstRead,
  elems, readers
  ) where

import Prelude hiding (read,elem)

-- | A "pipe" containing elements of type @a@, and readers of type @a -> r@.
data Pipe a r = Pipe {
  -- | Get the elements of a pipe.
  elems :: [a], 
  -- | Get the readers of a pipe.
  readers :: [a -> r]
  }

empty :: Pipe a r
-- | An empty pipe with no elements or readers.
empty = Pipe { elems = [], readers = [] }

-- | Create a pipe from a single element.
firstWrite :: a -> Pipe a r
firstWrite elem = Pipe { elems = [elem], readers = [] }

-- | Create a pipe from a single reader.
firstRead :: (a -> r) -> Pipe a r
firstRead k = Pipe { elems = [], readers = [k] }

-- | Write an element to a pipe, returning the new pipe and presentation results.
write :: a -> Pipe a r -> (Pipe a r, [r])
write elem p = (Pipe (elem : elems p) (readers p) , rs)
  where rs = map (\k -> k elem) (readers p)

-- | Attach a reader to a pipe, returning the new pipe and presentation results.
read :: (a -> r) -> Pipe a r -> (Pipe a r, [r])
read k p = (Pipe (elems p) (k : readers p) , rs)
  where rs = map k  (elems p)