{-# LANGUAGE GADTs #-} {-# LANGUAGE Rank2Types #-} ----------------------------------------------------------------------------- -- | -- Module : Data.Machine.Wye -- Copyright : (C) 2012 Edward Kmett, RĂșnar Bjarnason, Paul Chiusano -- License : BSD-style (see the file LICENSE) -- -- Maintainer : Edward Kmett -- Stability : provisional -- Portability : Rank-2 Types, GADTs -- ---------------------------------------------------------------------------- module Data.Machine.Wye ( -- * Wyes Wye, WyeT , Y(..) , wye , addX, addY , capX, capY ) where import Control.Category import Data.Machine.Process import Data.Machine.Type import Data.Machine.Is import Data.Machine.Source import Prelude hiding ((.),id) ------------------------------------------------------------------------------- -- Wyes ------------------------------------------------------------------------------- -- | The input descriptor for a 'Wye' or 'WyeT' data Y a b c where X :: Y a b a -- block waiting on the left input Y :: Y a b b -- block waiting on the right input Z :: Y a b (Either a b) -- block waiting on either input -- | A 'Machine' that can read from two input stream in a non-deterministic manner. type Wye a b c = Machine (Y a b) c -- | A 'Machine' that can read from two input stream in a non-deterministic manner with monadic side-effects. type WyeT m a b c = MachineT m (Y a b) c -- | Compose a pair of pipes onto the front of a 'Wye'. -- | Precompose a 'Process' onto each input of a 'Wye' (or 'WyeT'). -- -- This is left biased in that it tries to draw values from the 'X' input whenever they are -- available, and only draws from the 'Y' input when 'X' would block. wye :: Monad m => ProcessT m a a' -> ProcessT m b b' -> WyeT m a' b' c -> WyeT m a b c wye ma mb m = MachineT $ runMachineT m >>= \v -> case v of Yield o k -> return $ Yield o (wye ma mb k) Stop -> return Stop Await f X ff -> runMachineT ma >>= \u -> case u of Yield a k -> runMachineT . wye k mb $ f a Stop -> runMachineT $ wye stopped mb ff Await g Refl fg -> return . Await (\a -> wye (g a) mb $ encased v) X . wye fg mb $ encased v Await f Y ff -> runMachineT mb >>= \u -> case u of Yield b k -> runMachineT . wye ma k $ f b Stop -> runMachineT $ wye ma stopped ff Await g Refl fg -> return . Await (\b -> wye ma (g b) $ encased v) Y . wye ma fg $ encased v Await f Z ff -> runMachineT ma >>= \u -> case u of Yield a k -> runMachineT . wye k mb . f $ Left a Stop -> runMachineT mb >>= \w -> case w of Yield b k -> runMachineT . wye stopped k . f $ Right b Stop -> runMachineT $ wye stopped stopped ff Await g Refl fg -> return . Await (\b -> wye stopped (g b) $ encased v) Y . wye stopped fg $ encased v Await g Refl fg -> runMachineT mb >>= \w -> case w of Yield b k -> runMachineT . wye (encased u) k . f $ Right b Stop -> return . Await (\a -> wye (g a) stopped $ encased v) X . wye fg stopped $ encased v Await h Refl fh -> return . Await (\c -> case c of Left a -> wye (g a) (encased w) $ encased v Right b -> wye (encased u) (h b) $ encased v) Z . wye fg fh $ encased v -- | Precompose a pipe onto the left input of a wye. addX :: Monad m => ProcessT m a b -> WyeT m b c d -> WyeT m a c d addX p = wye p echo {-# INLINE addX #-} -- | Precompose a pipe onto the right input of a tee. addY :: Monad m => ProcessT m b c -> WyeT m a c d -> WyeT m a b d addY = wye echo {-# INLINE addY #-} -- | Tie off one input of a tee by connecting it to a known source. capX :: Monad m => SourceT m a -> WyeT m a b c -> ProcessT m b c capX s t = process (capped Right) (addX s t) {-# INLINE capX #-} -- | Tie off one input of a tee by connecting it to a known source. capY :: Monad m => SourceT m b -> WyeT m a b c -> ProcessT m a c capY s t = process (capped Left) (addY s t) {-# INLINE capY #-} -- | Natural transformation used by 'capX' and 'capY' capped :: (a -> Either a a) -> Y a a b -> a -> b capped _ X = id capped _ Y = id capped f Z = f {-# INLINE capped #-}