packages feed

machines (empty) → 0.1

raw patch · 21 files changed

+1237/−0 lines, 21 filesdep +basedep +comonaddep +containerssetup-changed

Dependencies added: base, comonad, containers, directory, doctest, filepath, free, mtl, profunctors, semigroups, transformers

Files

+ .ghci view
@@ -0,0 +1,1 @@+:set -isrc -idist/build/autogen -optP-include -optPdist/build/autogen/cabal_macros.h
+ .gitignore view
@@ -0,0 +1,4 @@+dist+TAGS+tags+docs
+ .travis.yml view
@@ -0,0 +1,8 @@+language: haskell+# Use this when hackage is down.+# before_install:+#   - mkdir -p ~/.cabal+#   - cp config ~/.cabal/config+#   - cabal update+notifications:+  irc: "irc.freenode.org#haskell-lens"
+ .vim.custom view
@@ -0,0 +1,21 @@+" Add the following to your .vimrc to automatically load this on startup+" if filereadable(".vim.custom")+"     so .vim.custom+" endif++function StripTrailingWhitespace()+  let myline=line(".")+  let mycolumn = col(".")+  silent %s/  *$//+  call cursor(myline, mycolumn)+endfunction++syntax on+set tags=TAGS;/+set listchars=tab:‗‗,trail:‗+set list++map <F2> :exec ":!hasktags -x -c --ignore src"<CR><CR>++au BufWritePre *.hs,*.markdown silent! cal StripTrailingWhitespace()+au BufWritePost *.hs silent! :exec ":!hasktags -x -c --ignore src"
+ CHANGELOG.markdown view
@@ -0,0 +1,3 @@+0.1+---+* Initial release
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright 2012 Edward Kmett, Runar Bjarnason, Paul Chiusano++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.++3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.
+ README.markdown view
@@ -0,0 +1,30 @@+machines+========++[![Build Status](https://secure.travis-ci.org/ekmett/machines.png?branch=master)](http://travis-ci.org/ekmett/machines)++Machines are demand driven input sources like pipes or conduits, but can support multiple inputs.++You design a `Machine` by writing a `Plan`. You then `construct` the machine.++Simple machines that take one input are called a `Process` and processes form a `Category`. More generally you can attach a+`Process` to the output of any type of `Machine`, yielding a new `Machine`.++More complicated machines provide other ways of connecting to them.++Typically the use of machines proceeds by using simple plans into machine `Tee`s and `Wye`s, capping many of the inputs to+those with possibly monadic sources, feeding the rest input (possibly repeatedly) and calling `run` or `runT` to get the+answers out.++There is a lot of flexibility when building a machine in choosing between empowering the machine to run its own monadic effects+or delegating that responsibility to a custom driver.+++Contact Information+-------------------++Contributions and bug reports are welcome!++Please feel free to contact me through github or on the #haskell IRC channel on irc.freenode.net.++-Edward Kmett
+ Setup.lhs view
@@ -0,0 +1,7 @@+#!/usr/bin/runhaskell+> module Main (main) where++> import Distribution.Simple++> main :: IO ()+> main = defaultMain
+ config view
@@ -0,0 +1,16 @@+-- This provides a custom ~/.cabal/config file for use when hackage is down that should work on unix+--+-- This is particularly useful for travis-ci to get it to stop complaining+-- about a broken build when everything is still correct on our end.+--+-- This uses Luite Stegman's mirror of hackage provided by his 'hdiff' site instead+--+-- To enable this, uncomment the before_script in .travis.yml++remote-repo: hdiff.luite.com:http://hdiff.luite.com/packages/archive+remote-repo-cache: ~/.cabal/packages+world-file: ~/.cabal/world+build-summary: ~/.cabal/logs/build.log+remote-build-reporting: anonymous+install-dirs user+install-dirs global
+ machines.cabal view
@@ -0,0 +1,74 @@+name:          machines+category:      Control, Enumerator+version:       0.1+license:       BSD3+cabal-version: >= 1.10+license-file:  LICENSE+author:        Edward A. Kmett, Rúnar Bjarnason+maintainer:    Edward A. Kmett <ekmett@gmail.com>+stability:     provisional+homepage:      http://github.com/ekmett/machines/+bug-reports:   http://github.com/ekmett/machines/issues+copyright:     Copyright (C) 2012 Edward A. Kmett+synopsis:      Networked stream transducers+description:   Networked stream transducers+build-type:    Simple+tested-with:   GHC == 7.4.1+extra-source-files:+  .travis.yml+  .ghci+  .gitignore+  .vim.custom+  config+  README.markdown+  CHANGELOG.markdown++source-repository head+  type: git+  location: git://github.com/ekmett/machines.git++library+  build-depends:+    base         == 4.*,+    comonad      == 3.0.*,+    containers   >= 0.3   && < 0.6,+    free         >= 3.1.1 && < 3.2,+    profunctors  == 3.0.*,+    semigroups   >= 0.8.3 && < 0.9,+    transformers == 0.3.*,+    mtl          >= 2.1.1 && < 2.2++  exposed-modules:+    Data.Machine+    Data.Machine.Is+    Data.Machine.Mealy+    Data.Machine.Moore+    Data.Machine.Process+    Data.Machine.Plan+    Data.Machine.Tee+    Data.Machine.Type+    Data.Machine.Unread+    Data.Machine.Wye++  default-language: Haskell2010+  other-extensions:+    FlexibleInstances+    GADTs+    MultiParamTypeClasses+    Rank2Types+    UndecidableInstances++  ghc-options: -Wall -fwarn-tabs -O2 -fdicts-cheap -funbox-strict-fields+  hs-source-dirs: src++-- Verify the results of the examples+test-suite doctests+  type:    exitcode-stdio-1.0+  main-is: doctests.hs+  build-depends:+    base == 4.*,+    directory >= 1.0 && < 1.2,+    doctest >= 0.8 && <= 0.9,+    filepath >= 1.3 && < 1.4+  ghc-options: -Wall -Werror -threaded+  hs-source-dirs: tests
+ src/Data/Machine.hs view
@@ -0,0 +1,32 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Data.Machine+-- Copyright   :  (C) 2012 Edward Kmett+-- License     :  BSD-style (see the file LICENSE)+--+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>+-- Stability   :  provisional+-- Portability :  non-portable+--+----------------------------------------------------------------------------+module Data.Machine+  ( module Data.Machine.Is+  , module Data.Machine.Moore+  , module Data.Machine.Mealy+  , module Data.Machine.Plan+  , module Data.Machine.Process+  , module Data.Machine.Source+  , module Data.Machine.Tee+  , module Data.Machine.Type+  , module Data.Machine.Wye+  ) where++import Data.Machine.Is+import Data.Machine.Mealy+import Data.Machine.Moore+import Data.Machine.Plan+import Data.Machine.Process+import Data.Machine.Source+import Data.Machine.Tee+import Data.Machine.Type+import Data.Machine.Wye
+ src/Data/Machine/Is.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE GADTs, TypeFamilies #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Data.Machine.Is+-- Copyright   :  (C) 2012 Edward Kmett+-- License     :  BSD-style (see the file LICENSE)+--+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>+-- Stability   :  provisional+-- Portability :  GADTs, Type Families+--+----------------------------------------------------------------------------+module Data.Machine.Is+  ( Is(..)+  ) where++import Control.Category+import Data.Monoid++-- | Witnessed type equality+data Is a b where+  Refl :: Is a a++instance Show (Is a b) where+  showsPrec _ Refl = showString "Refl"++instance Eq (Is a b) where+  Refl == Refl = True++instance Ord (Is a b) where+  Refl `compare` Refl = EQ++instance (a ~ b) => Monoid (Is a b) where+  mempty = Refl+  mappend Refl Refl = Refl++instance (a ~ b) => Read (Is a b) where+  readsPrec d = readParen (d > 10) (\r -> [(Refl,s) | ("Refl",s) <- lex r ])++instance Category Is where+  id = Refl+  Refl . Refl = Refl
+ src/Data/Machine/Mealy.hs view
@@ -0,0 +1,116 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Data.Machine.Mealy+-- Copyright   :  (C) 2012 Edward Kmett+-- License     :  BSD-style (see the file LICENSE)+--+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>+-- Stability   :  provisional+-- Portability :  portable+--+-- <http://en.wikipedia.org/wiki/Mealy_machine>+----------------------------------------------------------------------------+module Data.Machine.Mealy+  ( Mealy(..)+  , unfoldMealy+  , logMealy+  ) where++import Control.Applicative+import Control.Arrow+import Control.Category+import Data.Machine.Plan+import Data.Machine.Type+import Data.Machine.Process+import Data.Profunctor+import Data.Semigroup+import Data.Sequence as Seq+import Prelude hiding ((.),id)++-- | 'Mealy' machines+newtype Mealy a b = Mealy { runMealy :: a -> (b, Mealy a b) }++instance Functor (Mealy a) where+  fmap f (Mealy m) = Mealy $ \a -> case m a of+    (b, n) -> (f b, fmap f n)++instance Applicative (Mealy a) where+  pure b = r where r = Mealy (const (b, r))+  Mealy m <*> Mealy n = Mealy $ \a -> case m a of+    (f, m') -> case n a of+       (b, n') -> (f b, m' <*> n')+  m <* _ = m+  _ *> n = n++-- | A 'Mealy' machine modeled with explicit state.+unfoldMealy :: (s -> a -> (b, s)) -> s -> Mealy a b+unfoldMealy f = go where+  go s = Mealy $ \a -> case f s a of+    (b, t) -> (b, go t)++-- | slow diagonalization+instance Monad (Mealy a) where+  return b = r where r = Mealy (const (b, r))+  m >>= f = Mealy $ \a -> case runMealy m a of+    (b, m') -> (fst (runMealy (f b) a), snd (runMealy (m' >>= f) a))+  _ >> n = n++instance Profunctor Mealy where+  rmap = fmap+  lmap f (Mealy m) = Mealy $ \a -> case m (f a) of+    (b, n) -> (b, lmap f n)++instance Automaton Mealy where+  auto = construct . go where+    go (Mealy f) = await >>= \a -> case f a of+      (b, m) -> do+         yield b+         go m++instance Category Mealy where+  id = Mealy (\a -> (a, id))+  Mealy bc . Mealy ab = Mealy $ \ a -> case ab a of+    (b, nab) -> case bc b of+      (c, nbc) -> (c, nbc . nab)++instance Arrow Mealy where+  arr f = r where r = Mealy (\a -> (f a, r))+  first (Mealy m) = Mealy $ \(a,c) -> case m a of+    (b, n) -> ((b, c), first n)++instance ArrowChoice Mealy where+  left m = Mealy $ \a -> case a of+    Left l  -> case runMealy m l of+      (b, m') -> (Left b, left m')+    Right r -> (Right r, left m)+  right m = Mealy $ \a -> case a of+    Left l -> (Left l, right m)+    Right r -> case runMealy m r of+      (b, m') -> (Right b, right m')+  m +++ n = Mealy $ \a -> case a of+    Left b -> case runMealy m b of+      (c, m') -> (Left c, m' +++ n)+    Right b -> case runMealy n b of+      (c, n') -> (Right c, m +++ n')+  m ||| n = Mealy $ \a -> case a of+    Left b -> case runMealy m b of+      (d, m') -> (d, m' ||| n)+    Right b -> case runMealy n b of+      (d, n') -> (d, m ||| n')++-- | Fast forward a mealy machine forward+driveMealy :: Mealy a b -> Seq a -> a -> (b, Mealy a b)+driveMealy m xs z = case viewl xs of+  y :< ys -> case runMealy m y of+    (_, n) -> driveMealy n ys z+  EmptyL  -> runMealy m z++-- | Accumulate history.+logMealy :: Semigroup a => Mealy a a+logMealy = Mealy $ \a -> (a, h a) where+  h a = Mealy $ \b -> let c = a <> b in (c, h c)++instance ArrowApply Mealy where+  app = go Seq.empty where+    go xs = Mealy $ \(m,x) -> case driveMealy m xs x of+      (c, _) -> (c, go (xs |> x))
+ src/Data/Machine/Moore.hs view
@@ -0,0 +1,75 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Data.Machine.Moore+-- Copyright   :  (C) 2012 Edward Kmett+-- License     :  BSD-style (see the file LICENSE)+--+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>+-- Stability   :  provisional+-- Portability :  portable+--+-- <http://en.wikipedia.org/wiki/Moore_machine>+----------------------------------------------------------------------------+module Data.Machine.Moore+  ( Moore(..)+  , logMoore+  , unfoldMoore+  ) where++import Control.Applicative+import Control.Comonad+import Control.Monad+import Data.Machine.Plan+import Data.Machine.Type+import Data.Machine.Process+import Data.Monoid+import Data.Profunctor++-- | 'Moore' machines+data Moore a b = Moore b (a -> Moore a b)++-- | Accumulate the input as a sequence.+logMoore :: Monoid m => Moore m m+logMoore = h mempty where+  h m = Moore m (\a -> h (m <> a))++-- | Construct a Moore machine from a state valuation and transition function+unfoldMoore :: (s -> (b, a -> s)) -> s -> Moore a b+unfoldMoore f = go where+  go s = case f s of+    (b, g) -> Moore b (go . g)++instance Automaton Moore where+  auto = construct . go where+    go (Moore b f) = do+      yield b+      await >>= go . f++instance Functor (Moore a) where+  fmap f (Moore b g) = Moore (f b) (fmap f . g)++instance Profunctor Moore where+  rmap = fmap+  lmap f (Moore b g) = Moore b (lmap f . g . f)++instance Applicative (Moore a) where+  pure a = r where r = Moore a (const r)+  Moore f ff <*> Moore a fa  = Moore (f a) (\i -> ff i <*> fa i)+  m <* _ = m+  _ *> n = n++-- | slow diagonalization+instance Monad (Moore a) where+  return a = r where r = Moore a (const r)+  Moore a k >>= f = case f a of+    Moore b _ -> Moore b (k >=> f)+  _ >> m = m++instance Comonad (Moore a) where+  extract (Moore b _) = b+  extend f w@(Moore _ g) = Moore (f w) (extend f . g)++instance ComonadApply (Moore a) where+  Moore f ff <@> Moore a fa = Moore (f a) (\i -> ff i <*> fa i)+  m <@ _ = m+  _ @> n = n
+ src/Data/Machine/Plan.hs view
@@ -0,0 +1,147 @@+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Data.Machine.Plan+-- Copyright   :  (C) 2012 Edward Kmett, Rúnar Bjarnason+-- License     :  BSD-style (see the file LICENSE)+--+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>+-- Stability   :  provisional+-- Portability :  Rank-N Types, MPTCs+--+----------------------------------------------------------------------------+module Data.Machine.Plan+  (+  -- * Plans+    Plan+  , runPlan+  , PlanT(..)+  , yield+  , await+  , stop+  , awaits+  ) where++import Control.Applicative+import Control.Category+import Control.Monad (ap, MonadPlus(..))+import Control.Monad.Trans.Class+import Control.Monad.IO.Class+import Control.Monad.State.Class+import Control.Monad.Reader.Class+import Control.Monad.Error.Class+import Data.Functor.Identity+import Prelude hiding ((.),id)++-------------------------------------------------------------------------------+-- Plans+-------------------------------------------------------------------------------++-- | You can 'construct' a 'Plan' (or 'PlanT'), turning it into a+-- 'Data.Machine.Type.Machine' (or 'Data.Machine.Type.MachineT').+--+newtype PlanT k i o m a = PlanT+  { runPlanT :: forall r.+      (a -> m r) ->                                     -- Done a+      (o -> m r -> m r) ->                              -- Yield o (Plan k i o a)+      (forall z. (z -> m r) -> k i z -> m r -> m r) ->  -- forall z. Await (z -> Plan i o a) (k i z) (Plan k i o a)+      m r ->                                            -- Fail+      m r+  }++-- | A @'Plan' k i o a@ is a specification for a pure 'Machine', that reads inputs selected by @k@+-- with types based on @i@, writes values of type @o@, and has intermediate results of type @a@.+--+-- A @'PlanT' k i o a@ can be used as a @'PlanT' k i o m a@ for any @'Monad' m@.+--+-- It is perhaps easier to think of 'Plan' in its un-cps'ed form, which would+-- look like:+--+-- @+-- data 'Plan' k i o a+--   = Done a+--   | Yield o (Plan k i o a)+--   | forall z. Await (z -> Plan k i o a) (k i z) (Plan k i o a)+--   | Fail+-- @+type Plan k i o a = forall m. PlanT k i o m a++-- | Deconstruct a 'Plan' without reference to a 'Monad'.+runPlan :: PlanT k i o Identity a+        -> (a -> r)+        -> (o -> r -> r)+        -> (forall z. (z -> r) -> k i z -> r -> r)+        -> r+        -> r+runPlan m kp ke kr kf = runIdentity $ runPlanT m+  (Identity . kp)+  (\o (Identity r) -> Identity (ke o r))+  (\f k (Identity r) -> Identity (kr (runIdentity . f) k r))+  (Identity kf)++instance Functor (PlanT k i o m) where+  fmap f (PlanT m) = PlanT $ \k -> m (k . f)++instance Applicative (PlanT k i o m) where+  pure a = PlanT (\kp _ _ _ -> kp a)+  (<*>) = ap++instance Alternative (PlanT k i o m) where+  empty = PlanT $ \_ _ _ kf -> kf+  PlanT m <|> PlanT n = PlanT $ \kp ke kr kf -> m kp ke (\ks kir _ -> kr ks kir (n kp ke kr kf)) (n kp ke kr kf)++instance Monad (PlanT k i o m) where+  return a = PlanT (\kp _ _ _ -> kp a)+  PlanT m >>= f = PlanT (\kp ke kr kf -> m (\a -> runPlanT (f a) kp ke kr kf) ke kr kf)+  fail _ = PlanT (\_ _ _ kf -> kf)++instance MonadPlus (PlanT k i o m) where+  mzero = empty+  mplus = (<|>)++instance MonadTrans (PlanT k i o) where+  lift m = PlanT (\kp _ _ _ -> m >>= kp)++instance MonadIO m => MonadIO (PlanT k i o m) where+  liftIO m = PlanT (\kp _ _ _ -> liftIO m >>= kp)++instance MonadState s m => MonadState s (PlanT k i o m) where+  get = lift get+  put = lift . put+  state f = PlanT $ \kp _ _ _ -> state f >>= kp++instance MonadReader e m => MonadReader e (PlanT k i o m) where+  ask = lift ask+  reader = lift . reader+  local f m = PlanT $ \kp ke kr kf -> local f (runPlanT m kp ke kr kf)++instance MonadError e m => MonadError e (PlanT k i o m) where+  throwError = lift . throwError+  catchError m k = PlanT $ \kp ke kr kf -> runPlanT m kp ke kr kf `catchError` \e -> runPlanT (k e) kp ke kr kf++-- | Output a result.+yield :: o -> Plan k i o ()+yield o = PlanT (\kp ke _ _ -> ke o (kp ()))++-- | Wait for input.+--+-- @'await' = 'awaits' 'id'@+await :: Category k => Plan k i o i+await = PlanT (\kp _ kr kf -> kr kp id kf)++-- | Wait for a particular input.+--+-- @+-- awaits 'L'  :: 'Plan' 'T' (a, b) o a+-- awaits 'R'  :: 'Plan' 'T' (a, b) o b+-- awaits 'id' :: 'Plan' 'Data.Machine.Is.Is' i o i+-- @+awaits :: k i j -> Plan k i o j+awaits h = PlanT $ \kp _ kr -> kr kp h++-- | @'stop' = 'empty'@+stop :: Plan k i o a+stop = empty
+ src/Data/Machine/Process.hs view
@@ -0,0 +1,149 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE FlexibleInstances #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Data.Machine.Process+-- Copyright   :  (C) 2012 Edward Kmett+-- License     :  BSD-style (see the file LICENSE)+--+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>+-- Stability   :  provisional+-- Portability :  Rank 2 Types, GADTs+--+----------------------------------------------------------------------------+module Data.Machine.Process+  (+  -- * Processes+    Process+  , ProcessT+  , Automaton(..)+  , process+  -- ** Common Processes+  , after+  , supply+  , prepended+  , filtered+  , dropping+  , taking+  , droppingWhile+  , takingWhile+  , buffered+  ) where++import Control.Applicative+import Control.Category+import Control.Monad (liftM, when, replicateM_)+import Data.Foldable+import Data.Machine.Is+import Data.Machine.Plan+import Data.Machine.Type+import Prelude hiding ((.),id)++-------------------------------------------------------------------------------+-- Processes+-------------------------------------------------------------------------------++-- | A @'Process' a b@ is a stream transducer that can consume values of type @a@+-- from its input, and produce values of type @b@ for its output.+type Process a b = Machine Is a b++-- | A @'ProcessT' m a b@ is a stream transducer that can consume values of type @a@+-- from its input, and produce values of type @b@ and has side-effects in the+-- 'Monad' @m@.+type ProcessT m a b = MachineT m Is a b++-- | An 'Automaton' is can be automatically lifted into a 'Process'+class Automaton k where+  auto :: k a b -> Process a b++instance Automaton (->) where+  auto f = repeatedly $ do+    i <- await+    yield (f i)++instance Automaton Is where+  auto Refl = repeatedly $ do+    i <- await+    yield i++-- | A 'Process' that prepends the elements of a 'Foldable' onto its input, then repeats its input from there.+prepended :: Foldable f => f a -> Process a a+prepended = before id . traverse_ yield++-- | A 'Process' that only passes through inputs that match a predicate.+filtered :: (a -> Bool) -> Process a a+filtered p = repeatedly $ do+  i <- await+  when (p i) $ yield i++-- | A 'Process' that drops the first @n@, then repeats the rest.+dropping :: Int -> Process a a+dropping n = before id $ replicateM_ n await++-- | A 'Process' that passes through the first @n@ elements from its input then stops+taking :: Int -> Process a a+taking n = construct . replicateM_ n $ await >>= yield++-- | A 'Process' that passes through elements until a predicate ceases to hold, then stops+takingWhile :: (a -> Bool) -> Process a a+takingWhile p = repeatedly $ await >>= \v -> if p v then yield v else stop++-- | A 'Process' that drops elements while a predicate holds+droppingWhile :: (a -> Bool) -> Process a a+droppingWhile p = before id loop where+  loop = await >>= \v -> if p v then loop else yield v++-- | Chunk up the input into `n` element lists.+--+-- Avoids returning empty lists and deals with the truncation of the last group.+buffered :: Int -> Process a [a]+buffered = repeatedly . go [] where+  go [] 0  = stop+  go acc 0 = yield (reverse acc)+  go acc n = do+    i <- await <|> yield (reverse acc) *> stop+    go (i:acc) $! n-1++-- | Build a new 'Machine' by adding a 'Process' to the output of an old 'Machine'+--+-- @+-- after :: 'Process' a b   -> 'Process' b c -> 'Process' a c+-- after :: 'Data.Machine.Tee.Tee' a b c     -> 'Process' c d -> 'Data.Machine.Tee.Tee' a b d+-- after :: 'Machine' k a b -> 'Process' b c -> 'Machine' k a c+-- @+after :: Monad m => MachineT m k a b -> ProcessT m b c -> MachineT m k a c+after ma mp = MachineT $ runMachineT mp >>= \v -> case v of+  Stop          -> return Stop+  Yield o k     -> return $ Yield o (after ma k)+  Await f Refl ff -> runMachineT ma >>= \u -> case u of+    Stop          -> runMachineT $ after stopped ff+    Yield o k     -> runMachineT . after k $ f o+    Await g kg fg -> let mv = MachineT (return v) in+      return $ Await (\a -> after (g a) mv) kg (after fg mv)++-- | Feed a 'Process' some input.+supply :: Monad m => [a] -> ProcessT m a b -> ProcessT m a b+supply []         m = m+supply xxs@(x:xs) m = MachineT $ runMachineT m >>= \v -> case v of+  Stop -> return Stop+  Await f Refl _ -> runMachineT $ supply xs (f x)+  Yield o k -> return $ Yield o (supply xxs k)++-- |+-- Convert a machine into a process, with a little bit of help.+--+-- @'process' 'id' = 'id'@+--+-- @+-- 'process' 'Data.Machine.Tee.L' :: 'Data.Machine.Process.Process' a c -> 'Data.Machine.Tee.Tee' a b c+-- 'process' 'Data.Machine.Tee.R' :: 'Data.Machine.Process.Process' b c -> 'Data.Machine.Tee.Tee' a b c+-- 'process' 'id' :: 'Data.Machine.Process.Process' a b -> 'Data.Machine.Process.Process' a b+-- @+process :: Monad m => (forall a. k i a -> i' -> a) -> MachineT m k i o -> ProcessT m i' o+process f (MachineT m) = MachineT (liftM f' m) where+  f' (Yield o k)     = Yield o (process f k)+  f' Stop            = Stop+  f' (Await g kir h) = Await (process f . g . f kir) Refl (process f h)++
+ src/Data/Machine/Tee.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE Rank2Types #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Data.Machine.Tee+-- Copyright   :  (C) 2012 Edward Kmett, Rúnar Bjarnason, Paul Chiusano+-- License     :  BSD-style (see the file LICENSE)+--+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>+-- Stability   :  provisional+-- Portability :  Rank-2 Types, GADTs+--+----------------------------------------------------------------------------+module Data.Machine.Tee+  ( -- * Tees+    Tee, TeeT+  , T(..)+  , tee+  , addL, addR+  , capL, capR+  ) where++import Control.Category+import Data.Machine.Is+import Data.Machine.Process+import Data.Machine.Type+import Data.Machine.Source+import Prelude hiding ((.),id)++-------------------------------------------------------------------------------+-- Tees+-------------------------------------------------------------------------------++-- | The input descriptor for a 'Tee' or 'TeeT'+data T i c where+  L :: T (a, b) a+  R :: T (a, b) b++-- | A 'Machine' that can read from two input stream in a deterministic manner.+type Tee a b c = Machine T (a, b) c++-- | A 'Machine' that can read from two input stream in a deterministic manner with monadic side-effects.+type TeeT m a b c = MachineT m T (a, b) c++-- | Compose a pair of pipes onto the front of a Tee.+tee :: Monad m => ProcessT m a a' -> ProcessT m b b' -> TeeT m a' b' c -> TeeT m a b c+tee ma mb m = MachineT $ runMachineT m >>= \v -> case v of+  Stop         -> return Stop+  Yield o k    -> return $ Yield o $ tee ma mb k+  Await f L ff -> runMachineT ma >>= \u -> case u of+    Stop            -> runMachineT $ tee stopped mb ff+    Yield a k       -> runMachineT $ tee k mb $ f a+    Await g Refl fg ->+      return $ Await (\a -> tee (g a) mb $ encased v) L $ tee fg mb $ encased v+  Await f R ff -> runMachineT mb >>= \u -> case u of+    Stop            -> runMachineT $ tee ma stopped ff+    Yield b k       -> runMachineT $ tee ma k $ f b+    Await g Refl fg ->+      return $ Await (\b -> tee ma (g b) $ encased v) R $ tee ma fg $ encased v++-- | Precompose a pipe onto the left input of a tee.+addL :: Monad m => ProcessT m a b -> TeeT m b c d -> TeeT m a c d+addL p = tee p id++-- | Precompose a pipe onto the right input of a tee.+addR :: Monad m => ProcessT m b c -> TeeT m a c d -> TeeT m a b d+addR = tee id++-- | Tie off one input of a tee by connecting it to a known source.+capL :: Monad m => SourceT m a -> TeeT m a b c -> ProcessT m b c+capL s t = fit cappedT $ addL s t++-- | Tie off one input of a tee by connecting it to a known source.+capR :: Monad m => SourceT m b -> TeeT m a b c -> ProcessT m a c+capR s t = fit cappedT $ addR s t++-- | Natural transformation used by 'capL' and 'capR'.+cappedT :: T (a, a) b -> Is a b+cappedT R = Refl+cappedT L = Refl
+ src/Data/Machine/Type.hs view
@@ -0,0 +1,234 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE FlexibleInstances #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Data.Machine.Type+-- Copyright   :  (C) 2012 Edward Kmett+-- License     :  BSD-style (see the file LICENSE)+--+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>+-- Stability   :  provisional+-- Portability :  rank-2, GADTs+--+----------------------------------------------------------------------------+module Data.Machine.Type+  (+  -- * Machines+    MachineT(..)+  , Step(..)+  , Machine+  , runT_+  , runT+  , run+  , runMachine+  , encased++  -- ** Building machines from plans+  , construct+  , repeatedly+  , before+--  , sink++  -- * Reshaping machines+  , fit+  , pass++  , stopped+  ) where++import Control.Applicative+import Control.Category+import Control.Monad (liftM)+import Data.Foldable+import Data.Functor.Identity+import Data.Machine.Is+import Data.Machine.Plan+import Data.Monoid+import Data.Profunctor+import Prelude hiding ((.),id)++-------------------------------------------------------------------------------+-- Transduction Machines+-------------------------------------------------------------------------------++-- | This is the base functor for a 'Machine' or 'MachineT'.+--+-- Note: Machines are usually constructed from 'Plan', so this does not need to be CPS'd.+data Step k i o r+  = Stop+  | Yield o r+  | forall t. Await (t -> r) (k i t) r++instance Functor (Step k i o) where+  fmap _ Stop = Stop+  fmap f (Yield o k) = Yield o (f k)+  fmap f (Await g kg fg) = Await (f . g) kg (f fg)++-- | A 'MachineT' reads from a number of inputs and may yield results before stopping+-- with monadic side-effects.+newtype MachineT m k i o = MachineT { runMachineT :: m (Step k i o (MachineT m k i o)) }++-- | A 'Machine' reads from a number of inputs and may yield results before stopping.+--+-- A 'Machine' can be used as a @'MachineT' m@ for any @'Monad' m@.+type Machine k i o = forall m. Monad m => MachineT m k i o++-- | @'runMachine' = 'runIdentity' . 'runMachineT'@+runMachine :: MachineT Identity k i o -> Step k i o (MachineT Identity k i o)+runMachine = runIdentity . runMachineT++-- | Pack a Step of a Machine into a Machine.+encased :: Monad m => Step k i o (MachineT m k i o) -> MachineT m k i o+encased = MachineT . return++instance Monad m => Functor (MachineT m k i) where+  fmap f (MachineT m) = MachineT (liftM f' m) where+    f' (Yield o xs)    = Yield (f o) (f <$> xs)+    f' (Await k kir e) = Await (fmap f . k) kir (f <$> e)+    f' Stop            = Stop++{-+apMachines :: Maybe (Seq i) -> Seq a -> MachineT m k i (a -> b) -> MachineT m k i a -> MachineT m k i b+apMachines is as m n = MachineT $ runMachineT m >>= \u -> case u of+  Stop -> return Stop+  Yield f m' -> case viewl as of+    a :< as' -> return $ Yield (f a) (apMachines is as' m' n)+    EmptyL   -> paMachines is (Seq.singleton f) m' n+  Await k Refl kf -> case is of+    Nothing ->++paMachines :: Maybe (Seq i) -> Seq (a -> b) -> MachineT m k i (a -> b) -> MachineT m k i a -> MachineT m k i b+paMachines is fs m n = MachineT $ runMachineT n >>= \v -> case v of+  Stop -> return Stop+  Yield a n' -> case viewl fs of+    f :< fs' -> return $ Yield (f a) (paMachines is fs' m n')+    EmptyL   -> apMachines is (Seq.singleton a) m n'++instance (k ~ Is, Monad m) => Applicative (MachineT m k i) where+  pure = repeatedly . yield+  m <*>  n = runMachineT m >>= \u -> case u of+    Stop       -> return stopped+    Yield f xs ->+    Await f Refl ff ->+-}++instance (Monad m, Profunctor k) => Profunctor (MachineT m k) where+  rmap = fmap+  lmap f (MachineT m) = MachineT (liftM f' m) where+    f' (Yield o xs)    = Yield o (lmap f xs)+    f' (Await k kir e) = Await (lmap f . k) (lmap f kir) (lmap f e)+    f' Stop            = Stop++-- | Stop feeding input into model, taking only the effects.+runT_ :: Monad m => MachineT m k a b -> m ()+runT_ (MachineT m) = m >>= \v -> case v of+  Stop        -> return ()+  Yield _ k   -> runT_ k+  Await _ _ e -> runT_ e++-- | Stop feeding input into model and extract an answer+runT :: Monad m => MachineT m k a b -> m [b]+runT (MachineT m) = m >>= \v -> case v of+  Stop        -> return []+  Yield o k   -> liftM (o:) (runT k)+  Await _ _ e -> runT e++-- | Run a pure machine and extract an answer.+run :: MachineT Identity k a b -> [b]+run = runIdentity . runT++-- | This permits toList to be used on a Machine.+instance (m ~ Identity) => Foldable (MachineT m k i) where+  foldMap f (MachineT (Identity m)) = go m where+    go Stop = mempty+    go (Yield o k) = f o `mappend` foldMap f k+    go (Await _ _ fg) = foldMap f fg++-- |+-- Connect different kinds of machines.+--+-- @'fit' 'id' = 'id'@+--+-- @+-- 'fit' 'id' :: 'Data.Machine.Process.Process' a b -> 'Data.Machine.Process.Process' a b+-- @+fit :: Monad m => (forall a. k i a -> k' i' a) -> MachineT m k i o -> MachineT m k' i' o+fit f (MachineT m) = MachineT (liftM f' m) where+  f' (Yield o k)     = Yield o (fit f k)+  f' Stop            = Stop+  f' (Await g kir h) = Await (fit f . g) (f kir) (fit f h)++-- | Compile a machine to a model.+construct :: Monad m => PlanT k i o m a -> MachineT m k i o+construct m = MachineT $ runPlanT m+  (const (return Stop))+  (\o k -> return (Yield o (MachineT k)))+  (\f k g -> return (Await (MachineT . f) k (MachineT g)))+  (return Stop)++-- | Generates a model that runs a machine until it stops, then start it up again.+--+-- @'repeatedly' m = 'construct' ('Control.Monad.forever' m)@+repeatedly :: Monad m => PlanT k i o m a -> MachineT m k i o+repeatedly m = r where+  r = MachineT $ runPlanT m+    (const (runMachineT r))+    (\o k -> return (Yield o (MachineT k)))+    (\f k g -> return (Await (MachineT . f) k (MachineT g)))+    (return Stop)++-- | Evaluate a machine until it stops, and then yield answers according to the supplied model.+before :: Monad m => MachineT m k i o -> PlanT k i o m a -> MachineT m k i o+before (MachineT n) m = MachineT $ runPlanT m+  (const n)+  (\o k -> return (Yield o (MachineT k)))+  (\f k g -> return (Await (MachineT . f) k (MachineT g)))+  (return Stop)++-- | Given a handle, ignore all other inputs and just stream input from that handle.+--+-- @+-- 'pass' 'id' :: 'Data.Machine.Process.Process' a a+-- 'pass' 'Data.Machine.Tee.L'  :: 'Data.Machine.Tee.Tee' a b a+-- 'pass' 'Data.Machine.Tee.R'  :: 'Data.Machine.Tee.Tee' a b b+-- 'pass' 'Data.Machine.Wye.X'  :: 'Data.Machine.Wye.Wye' a b a+-- 'pass' 'Data.Machine.Wye.Y'  :: 'Data.Machine.Wye.Wye' a b b+-- 'pass' 'Data.Machine.Wye.Z'  :: 'Data.Machine.Wye.Wye' a b (Either a b)+-- @+pass :: k i o -> Machine k i o+pass input = repeatedly $ do+  a <- awaits input+  yield a++-- | Eventually this will probably revert to @instance 'Monad' m => 'Category' ('MachineT' m 'Is')@+instance (k ~ Is, Monad m) => Category (MachineT m k) where+  id = repeatedly $ do+    i <- await+    yield i++  m . n = MachineT $ runMachineT m >>= \v -> case v of+    Stop          -> return Stop+    Yield a as    -> return $ Yield a (as . n)+    Await f Refl k -> runMachineT n >>= \u -> case u of+      Stop          -> runMachineT $ k . stopped+      Yield b bs    -> runMachineT $ f b . bs+      Await g Refl fg -> return $ Await (\a -> encased v . g a) Refl (encased v . fg)++-- | This is a stopped 'Machine'+stopped :: Machine k a b+stopped = encased Stop++-------------------------------------------------------------------------------+-- Sink+-------------------------------------------------------------------------------++{-+-- |+-- A Sink in this model is a 'Data.Machine.Process.Process'+-- (or 'Data.Machine.Tee.Tee', etc) that produces a single answer.+--+-- \"Is that your final answer?\"+sink :: Monad m => (forall o. PlanT k i o m a) -> MachineT m k i a+sink m = runPlanT m (\a -> Yield a Stop) id (Await id) Stop+-}
+ src/Data/Machine/Unread.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE GADTs #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Data.Machine.Unread+-- Copyright   :  (C) 2012 Edward Kmett+-- License     :  BSD-style (see the file LICENSE)+--+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>+-- Stability   :  provisional+-- Portability :  GADTs+--+----------------------------------------------------------------------------+module Data.Machine.Unread+  ( Unread(..)+  , peek+  , unread+  ) where++import Data.Machine.Plan++-- | This is a simple process type that knows how to push back input.+data Unread a r where+  Unread :: a -> Unread a ()+  Read   :: Unread a a++-- | Peek at the next value in the input stream without consuming it+peek :: Plan Unread a b a+peek = do+  a <- awaits Read+  awaits (Unread a)+  return a++-- | Push back into the input stream+unread :: a -> Plan Unread a b ()+unread a = awaits (Unread a)++-- TODO: make this a class?
+ src/Data/Machine/Wye.hs view
@@ -0,0 +1,103 @@+{-# 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 <ekmett@gmail.com>+-- 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 i 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 id++-- | 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 id++-- | 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)++-- | 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)++-- | 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
+ tests/doctests.hs view
@@ -0,0 +1,28 @@+module Main where++import Test.DocTest+import System.Directory+import System.FilePath+import Control.Applicative+import Control.Monad+import Data.List++main :: IO ()+main = getSources >>= \sources -> doctest $+    "-isrc"+  : "-idist/build/autogen"+  : "-optP-include"+  : "-optPdist/build/autogen/cabal_macros.h"+  : sources++getSources :: IO [FilePath]+getSources = filter (isSuffixOf ".hs") <$> go "src"+  where+    go dir = do+      (dirs, files) <- getFilesAndDirectories dir+      (files ++) . concat <$> mapM go dirs++getFilesAndDirectories :: FilePath -> IO ([FilePath], [FilePath])+getFilesAndDirectories dir = do+  c <- map (dir </>) . filter (`notElem` ["..", "."]) <$> getDirectoryContents dir+  (,) <$> filterM doesDirectoryExist c <*> filterM doesFileExist c