automaton 1.3 → 1.4
raw patch · 7 files changed
+106/−6 lines, 7 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Data.Automaton: unfold_ :: Applicative m => s -> (a -> s -> s) -> Automaton m a s
+ Data.Automaton.Trans.Accum: accumS :: (Functor m, Monad m) => Automaton m (w, a) (w, b) -> Automaton (AccumT w m) a b
+ Data.Automaton.Trans.Accum: runAccumS :: (Functor m, Monad m) => Automaton (AccumT w m) a b -> Automaton m (w, a) (w, b)
+ Data.Automaton.Trans.Accum: runAccumS_ :: (Functor m, Monoid w, Monad m) => Automaton (AccumT w m) a b -> Automaton m a (w, b)
+ Data.Automaton.Trans.Accum: runAccumS__ :: (Functor m, Monoid w, Monad m) => Automaton (AccumT w m) a b -> Automaton m a b
Files
- CHANGELOG.md +8/−2
- automaton.cabal +5/−3
- src/Data/Automaton.hs +10/−0
- src/Data/Automaton/Trans/Accum.hs +60/−0
- test/Automaton.hs +2/−0
- test/Automaton/Except.hs +1/−1
- test/Automaton/Trans/Accum.hs +20/−0
CHANGELOG.md view
@@ -1,5 +1,11 @@ # Revision history for automaton -## 0.1.0.0+## 1.4 -* Initial version ;)+* Added `Data.Automaton.Trans.Accum`+* Added `unfold_`+* Backwards compatible to 1.3, but to keep version numbers in sync with rhine, the automaton version has also been bumped++## 1.3++* Initial version
automaton.cabal view
@@ -1,11 +1,11 @@ cabal-version: 3.0 name: automaton-version: 1.3+version: 1.4 synopsis: Effectful streams and automata in initial encoding description: Effectful streams have an internal state and a step function. Varying the effect type, this gives many different useful concepts:- For example with a reader effect, it results in automata/transducers/state machines.+ For example with a reader effect, it results in automata (a.k.a transducers or state machines). license: MIT license-file: LICENSE@@ -24,7 +24,7 @@ source-repository this type: git location: https://github.com/turion/rhine.git- tag: v1.3+ tag: v1.4 common opts build-depends:@@ -65,6 +65,7 @@ exposed-modules: Data.Automaton Data.Automaton.Final+ Data.Automaton.Trans.Accum Data.Automaton.Trans.Except Data.Automaton.Trans.Maybe Data.Automaton.Trans.RWS@@ -93,6 +94,7 @@ other-modules: Automaton Automaton.Except+ Automaton.Trans.Accum Stream build-depends:
src/Data/Automaton.hs view
@@ -111,6 +111,16 @@ Automaton m a b unfoldM state step = Automaton $! Stateful $! StreamT {state, step = \s -> ReaderT $ \a -> step a s} +-- | Like 'unfold', but output the current state.+unfold_ ::+ (Applicative m) =>+ -- | The initial state+ s ->+ -- | The step function+ (a -> s -> s) ->+ Automaton m a s+unfold_ state step = unfold state $ \a s -> let s' = step a s in Result s' s'+ instance (Eq s, Floating s, VectorSpace v s, Applicative m) => VectorSpace (Automaton m a v) (Automaton m a s) where zeroVector = Automaton zeroVector Automaton s *^ Automaton v = coerce $ s *^ v
+ src/Data/Automaton/Trans/Accum.hs view
@@ -0,0 +1,60 @@+{- | Handle a global 'AccumT' layer in an 'Automaton'.++A global accumulation state can be hidden by an automaton by making it an internal state.+-}+module Data.Automaton.Trans.Accum (+ module Control.Monad.Trans.Accum,+ accumS,+ runAccumS,+ runAccumS_,+ runAccumS__,+)+where++-- base+import Control.Arrow (arr, returnA, (>>>))++-- transformers+import Control.Monad.Trans.Accum++-- automaton+import Data.Automaton (Automaton, feedback, withAutomaton)+import Data.Stream.Result (Result (..))++{- | Convert from explicit states to the 'AccumT' monad transformer.++The original automaton is interpreted to take the current accumulated state as input and return the log to be appended as output.++This is the opposite of 'runAccumS'.+-}+accumS :: (Functor m, Monad m) => Automaton m (w, a) (w, b) -> Automaton (AccumT w m) a b+accumS = withAutomaton $ \f a -> AccumT $ \w ->+ (\(Result s (w', b)) -> (Result s b, w'))+ <$> f (w, a)++{- | Make the accumulation transition in 'AccumT' explicit as 'Automaton' inputs and outputs.++This is the opposite of 'accumS'.+-}+runAccumS :: (Functor m, Monad m) => Automaton (AccumT w m) a b -> Automaton m (w, a) (w, b)+runAccumS = withAutomaton $ \f (w, a) ->+ (\(Result s b, w') -> Result s (w', b))+ <$> runAccumT (f a) w++{- | Convert global accumulation state to internal state of an 'Automaton'.++The current state is output on every step.+-}+runAccumS_ ::+ (Functor m, Monoid w, Monad m) =>+ -- | An automaton with a global accumulation state effect+ Automaton (AccumT w m) a b ->+ Automaton m a (w, b)+runAccumS_ automaton = feedback mempty $ proc (a, wState) -> do+ (wAdd, b) <- runAccumS automaton -< (wState, a)+ let wState' = wState <> wAdd+ returnA -< ((wState', b), wState')++-- | Like 'runAccumS_', but don't output the current accum.+runAccumS__ :: (Functor m, Monoid w, Monad m) => Automaton (AccumT w m) a b -> Automaton m a b+runAccumS__ automaton = runAccumS_ automaton >>> arr snd
test/Automaton.hs view
@@ -28,6 +28,7 @@ -- automaton import Automaton.Except+import Automaton.Trans.Accum import Data.Automaton import Data.Automaton.Final import Data.Automaton.Trans.Maybe@@ -70,6 +71,7 @@ , testCase "sumN" $ runIdentity (embed (arr (const (1 :: Integer)) >>> sumN) [(), (), ()]) @?= [1, 2, 3] , testCase "lastS" $ runIdentity (embed (lastS 0) [Nothing, Just 10]) @?= [0, 10] , Automaton.Except.tests+ , Automaton.Trans.Accum.tests ] inMaybe :: Automaton Maybe (Maybe a) a
test/Automaton/Except.hs view
@@ -9,7 +9,7 @@ -- tasty-hunit import Test.Tasty.HUnit (testCase, (@?=)) --- rhine+-- automaton import Data.Automaton (embed) import Data.Automaton.Trans.Except (safe, safely, step)
+ test/Automaton/Trans/Accum.hs view
@@ -0,0 +1,20 @@+module Automaton.Trans.Accum where++-- base+import Control.Monad.Identity (Identity (runIdentity))+import Data.Monoid (Sum (..))++-- transformers+import Control.Monad.Trans.Accum (add, look)++-- tasty+import Test.Tasty (testGroup)++-- tasty-hunit+import Test.Tasty.HUnit (testCase, (@?=))++-- automaton+import Data.Automaton+import Data.Automaton.Trans.Accum (runAccumS_)++tests = testGroup "Trans.Accum" [testCase "runAccumS_" $ runIdentity (embed (runAccumS_ (constM (add (Sum 1) >> look))) (replicate 5 ())) @?= (\n -> (n, n)) <$> [1, 2, 3, 4, 5]]